PandemicMartí Oller |
The game is set in the dystopian year 2025, when a new virus emerged. The new virus was the last straw for an already frail civilization. As a consequence, society collapsed, the stock market crashed and, most tragically, billions of people died. Out of the few survivors, four rival factions have emerged and are fighting against each other for the control of a society left in shambles. Who will win?
In this game, four players compete to dominate the ruins of society. The winner of the match is the one that gets the highest score in the end.
The game is played on a map represented by a (randomly generated) square board. The cells of the map can be of four different types: WALL, GRASS, CITY or PATH. Cells of the type CITY are grouped into rectangles representing cities, hence the name. Similarly, PATH cells are grouped into sequences that form non-intersecting paths that connect cities. Finally, there are WALL cells surrounding the map, and some additional interior ones resembling fallen cities and paths that are now inaccessible.
Each player controls a number of units, which can be moved every round. Any player that tries to give more than 1000 instructions in the same round will be aborted. Units can remain still or move in any of the four cardinal directions (top, right, bottom or left), as long as there isn’t a wall. If a unit receives more than one instruction, all but the first will be ignored.
Cells can be occupied by at most one unit. Hence, a unit A can’t just simply move to a cell where there is another unit B. If A tries to move to a cell occupied by B, and both units belong to the same player, nothing will happen (A will not move). However, if they belong to different players, then A will attack B, and B will lose between 25 and 40 points of health (randomly with uniform probability). If B reaches a negative amount of health due to the attack, unit B dies and regenerates under the control of unit A’s player, and A moves to B’s position. Otherwise, B survives and A doesn’t move after the attack.
Every five rounds, a mask spawns in a grass cell. Masks are represented in the map as small black dots. If a unit that doesn’t have a mask moves to a cell where there is a mask, it will pick it up and wear it automatically. If the unit is already wearing a mask, it will just ignore it. If a unit wearing a mask dies, the mask disappears.
Points are obtained by conquering cities and paths. Initially, they are all empty and have no owner. As the game progresses, units can move there and conquer them. At the end of every round, the number of units of each player in every city and path is computed. If a player has strictly more units than any other player in a given city or path, the player conquers it. In case of a tie, the owner of the city or path doesn’t change from the previous round.
Every round, points are added to each player depending on the cities and paths they own. For every city under the control of a player, a total of bonus_per_city_cell()× size of the city points are added to her. Similarly, every path gives the player a total of bonus_per_path_cell()× size of the path. Additionally, for each player, her graph of conquests is considered. In this graph, the vertices are the cities of that player, and the edges are conquered paths connecting conquered cities. For each connected component of that graph with size i, additional 2i × factor_connected_component() points are added.
For instance, consider the state given by the screenshot in Figure ??. The red player will obtain the following number of points:
In total, the red player will receive 231 points this round.
Some of the units are infected with a potentially deadly virus, which they can spread to locations near them.
A unit that is infected by the virus will lose a fixed amount of health every round, which can vary between 2 and 5 (but is fixed for a given infected unit). If the unit’s health points decrease to a negative number due to the virus, the unit dies and respawns under the control of a random player (decided randomly with uniform probability 1/4). However, units may also recover from the virus. If a unit has been infected by the virus for t turns, the probability p of healing from the virus is computed as follows:
p = min | ⎛ ⎜ ⎜ ⎝ | 1, 0.001 | ⎛ ⎜ ⎜ ⎝ |
| + 1 | ⎞ ⎟ ⎟ ⎠ | ⎞ ⎟ ⎟ ⎠ |
What this formula means is that a unit that has had the virus for longer is more likely to recover from the infection. In practice, the formula implies that roughly 50% of infections will disappear within the first 31 turns, and approximately 90% of them will have disappeared by the 47th turn (provided the unit is still alive).
A unit that recovers from the virus becomes immune from the infection, and cannot catch the virus again.
Infected units can also propagate the virus to their surroundings. Every cell c has a certain amount of virus in it, which is represented by an integer c.virus initially set to zero. In the map, cells with higher concentrations of the virus are represented by darker colors. Every turn, c.virus is updated using the following procedure:
Units can get infected by being in cells with non-zero amounts of virus. The probability p1 that a non-masked, non-immune unit will catch the virus in a cell with virus amount v is p1 = v/factor_infection(). If the susceptible unit is wearing a mask, that probability becomes p2 = p1/mask_protection(). With the default parameters, these numbers are p1 = v/50 and p2 = v/1000. Immune units can’t be reinfected, and units that are currently infected can’t get reinfected in any way.
Every time a unit gets infected by standing in a cell with virus in it, the amount of health they will lose in each turn is set to a number between 2 and 5 with uniform probability. This amount is called the damage, and does not change for a given infected unit.
When a unit dies (either due to an attack or the virus), it will spawn with full health in a border of the map. All spawned units have a 20% chance of having the virus, and the damage is set to a number between 2 and 4 (not 5, in this case) with uniform probability.
Initially, at round 0, exactly three units of each player will have the virus, with virus damage set to 2, 3 and 4, respectively.
In general, the execution of a round is done following these steps:
A game is defined by a board and the following set of parameters, whose default values are shown in parenthesis:
Unless there is a force majeure event, these are the values of parameters that will be used in the game.
The first thing you should do is to download the source code. This source code includes a C++ program that runs the matches and also an HTML viewer to watch them in a nice animated format. Also, a “Null” player and a “Demo” player are provided to make it easier to start coding your own player.
Here we will explain how to run the game under Linux, but a similar procedure should work as well under Windows, Mac, FreeBSD, OpenSolaris... The only requirements on your system are g++, make and a modern browser like Mozilla Firefox or Google Chrome.
To run your first match, follow the next steps:
cp AIDummy.o.Linux64 AIDummy.o
to copy the object file for player “Dummy” (copy AIDummy.o.MacOS if you use Mac).
make all to build the game and all the players. Note that Makefile identifies any file matching AI*.cc as a player.
Note: To get a larger view of the match, put your browser into full screen mode (press key F11).
Use
./Game --help
to see the list of parameters you can use. Particularly useful is
./Game --list
to show all the recognized player names. If needed, remember you can run
make clean
to delete the executable and object files and start over the build.
To create a new player with, say, name Manolito, copy AINull.cc (an empty player that is provided as a template) to a new file AIManolito.cc. Then, edit the new file and change the
line to
The name you choose for your player must be unique, non-offensive and less than 12 letters long. It will be used to define a new class PLAYER_NAME, which will be referred to below as your player class. The name will be shown as well when viewing the matches and on the website.
Now you can start implementing the method play(). This method will be called every round and is where your player should decide what to do, and do it. Of course, you can define auxiliary methods and variables inside your player class, but the entry point of your code will always be this play() method.
From your player class you can also call functions to access the board state, as defined in the State class in State.hh, and to command your units, as defined in the Action class in Action.hh. These functions are made available to your code using multiple inheritance. The documentation on the available functions can be found in the aforementioned header files. You can also examine the code of the “Demo” player in AIDemo.cc as an example of how to use these functions. Finally, it may be worth as well to have a look at the files Structs.hh for useful data structures, Random.hh for random generation utilities, Settings.hh for looking up the game settings and Player.hh for the me() method.
Note that you should not modify the factory() method from your player class, nor the last line that adds your player to the list of registered players.
In order to try your strategy against the Dummy player, we provide you with its object file. Hence, you do not have access to its source code but can add it as a player and compete against it.
As we have already mentioned, in order to add the Dummy player to the list of registered users, you must copy the file corresponding to your architecture to AIDummy.o. For example:
cp AIDummy.o.Linux64 AIDummy.o
Pro tip: ask you friend their object files (never source code!!!) and add them to your Makefile.
Once you think your player is strong enough to enter the competition, you should submit it to the Jutge.org website (https://www.jutge.org). Since it will run in a secure environment to prevent cheating, some restrictions apply to your code:
Any detected plagiarism will result in an overall grade of 0 in the course (not only in the Game) of all involved students. Additional disciplinary measures might also be taken. If student A and B are involved, measures will be applied to both of them, independently of who created the original code. No exceptions will be made under any circumstances.