This is where I ramble about how the server is put together...
The whole system is backed by a MySQL database (version 5.x) with a handful of tables used for results storage, scoring and rankings.
This table contains a list of all bots used in any of the rumbles, past or present. It's basically a lookup table so that all the others can simply store a bot ID value and the name and author information are stored here.
This is the archive of all uploaded battle results. Whenever a client uploads a new battle result it gets stored here in practically the same format as it arrived (with the exception of the bot names being replaced by their unique IDs).
Actually, two records are stored per battle, one for each bot. I plan to cut this down to one in the future, but this shortcut saved me significant implementation time and less opportunity for bugs. Now only saves one record per battle!
I may also extract the user data (username, IP, client version) and save them separately similar to the 'bot_data' table to cut down on storage requirements. Similar to bot names, user data is now saved in a separate table. This saves considerable space and has the added bonus of making the battle results table have a fixed row length.
There is also a flag for each battle result, which is initially off and will be set later once this result has been scored.
I tried to think of ways around having this table, but couldn't find anything more elegant. It basically summarizes the battle results for each pairing in every game, including average score and number of wins (for PL scoring, coming soon).
Like the battle results table, there is a duplicate of each pairing (one for each bot) which will eventually be cut down to one. There is also an update flag but its currently unused since pairings are updated as soon as new results arrive.
This table stores the participant list for each game, their current status (active/retired) and their latest scores. It's your one-stop-shop for rumble updates and is a very popular table indeed.
Bots get added here as soon as their first rumble result (in a particular game) has been received. But scores don't get updated until the periodic scoring process has completed, so there could be a lag between appearance and first scores.
Storage for flags and values that may change (without requiring source file changes). For example, the scoring update interval and size, or the server maintenance flag.
Everything on the server is done using PHP. I picked it because I know it well, it's designed for web applications and it's ubiquitous. No language flame wars please.
Downloading ratings files
When the client starts up, one of its first tasks is to get the current server rankings. A simple query on the 'participants' table serves up the rankings with minimal fuss.
Removing old participants
If the rankings contain bots that aren't on the participants list, the client will send a request to remove those participants. This is done by setting all battle results and pairings involving that bot to an 'R' status (retired), which excludes them from future queries. The same is done for the participants list.
Note that this means ratings won't change once a bot is removed. APS will eventually change, but only once each bot has a new pairing score update.
If a participant is reactivated, it will return to the rumble with all of its old scores intact. Other bots' APS will adjust to include it on the next scoring update.
Uploading battle results
When a client uploads a new battle result, it passes through a series of checks before it can be stored in the system.
Two new records are inserted into the results table. The pairing records (if they exist) are also updated to include the new scores.
This process adds new entries to the 'bot_data', 'game_pairings' and 'participants' tables as needed. Also, if a retired bot's results are uploaded, that will cause the bot to be reactivated in the system (see above).
As of SVN revision 11, scoring is now done on every upload. This is somewhat expensive, since it requires retrieving all pairings for the two bots from the battle just uploaded. APS, Elo and Glicko ratings are all recalculated and saved to the 'participants' table.
All of the display pages are fairly simple, getting their data from the 'participants' list and possibly the 'game_pairings' or 'battle_results' tables.
Update: this routine still exists (if we need to rebuild) but no longer runs by default. Scoring is now done on every upload.
This is the ugly routine because it contains all of the really slow queries. It will be initiated by any of the above if enough time has passed since the last update.
All "new" battle results (up to a maximum size, oldest first) will be read and grouped by bot. Then for each bot with new results, new rating scores will be calculated. New pairings results (ie. all pairings for that bot) will also be calculated. The new scores will then be saved to the participants list. Once all bots are done, the "new" battle results will be marked "rated".
Areas for Improvement
Update: As of rev 11, scoring is now done incrementally. The old update code still exists for rebuilding, if necessary.
This is my biggest headache at the moment. The current scheme is a bit odd, and slow to catch up during a rebuild. But it does support rebuilding, which is nice.
- Incremental updates: easiest to implement, just update the scores & ratings as new results are uploaded. Benefit of always being up to date. Downside is that it duplicates some queries (eg. the same bot may have other results recently uploaded so re-scoring the pairings will be multiple times in succession) and doesn't support in-sequence rebuilding.
- Incremental ratings + periodic pairings: combining the current method with the incremental one, ratings would be done incrementally and pairings (APS) would be done on a periodic basis. Best of both worlds? Rebuilding ratings would be done out-of-order though.