In my application I have multiple not realtime "games". In each game, each player has a score. During the course of the game, players may take turns doing some action.
However, if no actions are taken within a customizable amount of time (anywhere from 1 hour to 1 month), then both players will lose x amount of points.
I thought to have a global "timer" that is called by a cron job. At an interval of 1 hour, the timer will call each game's deductPoints() function.
Within the deductPoints() function I will do something like this:
deductPoints()
{
timeSinceLastDeduction++;
if(timeSinceLastDeduction >= frequencyOfDeduction)
{
deduct();
timeSinceLastDeduction = 0;
}
}
Is there a better way to accomplish this task?
If you are willing to store more data, I would suggest this design.
Instead of deducting the points every time (relying strongly on the "state" of the game), instead, store every action that the players have taken. Ex, lets take this table:
id | playerid | action | stamp
----+----------+----------+------------------------
1 | 1 | e2 to e4 | 2011-10-27 04:00:00-04
2 | 1 | | 2011-10-27 04:30:00-04
3 | 1 | | 2011-10-27 06:00:00-04
4 | 1 | | 2011-10-27 07:45:00-04
5 | 1 | | 2011-10-27 08:00:00-04
Etc. etc.
e2 to e4 is a Chess move by the way, one of the common opening moves. Your game, I can't really design that. The "action" is optional however, you only really need to store the fact that an action occured to calculate the penalty. (But might as well store extra information, as long as you have the space to do so of course).
"id" is the surrogate key in this case. In Postgresql, the DDL would look like:
CREATE TABLE actions(
id BIGSERIAL PRIMARY KEY,
playerid int,
action text,
stamp timestamptz,
UNIQUE(playerid, stamp));
The tuple (playerid,stamp) will be the "real" primary key. "id" is the surrogate key.
To see the difference in timestamps, the code is relatively simple using window functions. (Continuing to use PostgreSQL)
select playerid, time_between, id FROM
(SELECT playerid,
stamp - lag(stamp) OVER () as time_between,
id FROM actions
ORDER BY stamp)
as ss
WHERE playerid = 1
AND time_between > '1 hour';
Sample Output, given the test data from earlier:
playerid | time_between | id
----------+--------------+----
1 | 01:30:00 | 3
1 | 01:45:00 | 4
You don't have cron jobs at all. You stored more data (so it will be easier to "undo" actions). Your players can review past moves if they want to. If calculating the penalty becomes inefficient, then you can always just cache the score at a higher level.
Window Functions are not available in MySQL btw. But are available in PostgreSQL, SQL Server and Oracle.
EDIT: If space is a concern, then you can always "garbage collect" this table regularly. Still, that'd be a lot of games. Each row takes up ~48 bytes (assuming a null "action"). If you had 1000 players, each playing 200 games with 40 actions, you'd only have ~384MB of data.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With