Mad Max Revisited

Albert Oliveras

image

Game rules

This is a game for four players. Each player commands a group of cars in the dangerous roads of Wasteland. The goal is to get the highest possible score by collecting water bonuses and killing other players’ cars. However, gas bonuses also have to be collected in order to keep the cars running and tyres should be avoided to prevent collisions.

The game is played on a bidimensional cylindric universe. After unfolding, this universe can be seen as the result of setting side by side the same rectangle repeatedly along the vertical edge:

The cylindrical universe after unfolding.

This repeated rectangle is an n×mn \times m grid of cells, where nn is the number of rows and mm the number of columns. The leftmost upper cell is (0,0)(0,0) and the rightmost lower cell is (n1,m1)(n-1,m-1). Any pair (i,j)(i, j) such that 0i<n0 \leq i < n determines the position of a cell of the universe. Note that two positions (i,j)(i,j) and (i,j)(i,j') such that (jj)modm=0(j - j')\;\textrm{mod}\; m = 0 actually refer to the same cell. In what follows we will refer to ii and jj as the row and the column of the position (i,j)(i, j), respectively.

Each cell of this universe may be empty or may contain:

Players can look up the content of any cell of the universe during the match.

Each player commands a number of cars which is a parameter of the game. Each car has a unique identifying number. The cars commanded by the same player have consecutive identifiers.

Moving and shooting

At each round a player can command any of its cars to move in a particular direction or to shoot a missile (but not the two at the same time).

In general, the movement of the elements on the universe follows the next rules:

A car may shoot a missile if it has at least one in its stock (which is then consumed). There is an initial number of missiles in the stock of each car. This stock can be enlarged by means of missile bonuses. When a car shoots, the new missile automatically moves forward two columns from the car position, and then immediately after, the car also moves with the default direction.

Collisions in a cell

When a car and a bonus coincide in the same cell, the car consumes the bonus (increasing the number of available missiles if it is a missile bonus, the number of gas units if it is a gas bonus, or getting more score if it is a water bonus). Once a bonus is consumed, if possible it reappears on a random position outside the window. This random position is guaranteed to be previously empty and such that its surrounding square 5×55 \times 5 is free from missiles or cars.

In the other possible cases of collision, when two elements coincide in the same cell both are destroyed. In particular, if one of the colliding elements is a missile shot by a car of player AA and the other element is a car of a different player BB, then AA increases its score.

When a car is killed, it regenerates if possible after a number of rounds, on a random position of the window. This random position is guaranteed to be previously empty and such that its surrounding square 5×55 \times 5 is free from missiles, cars or tyres. The number of available missiles is the same as before it died and the gas units are determined by initial_gas()initial\_gas().

As a final consideration regarding collisions, when in a round a car (or a missile) moves from an initial cell to a final cell, in some cases it is considered that it also passes certain intermediate cells. More specifically:

  1. When a car moves from cell (i,j)(i, j) to cell (i+1,j+1)(i+1, j+1), it also passes through cells (i+1,j)(i+1,j) and (i,j+1)(i, j+1). Similarly when it moves to cell (i1,j+1)(i-1, j+1).

  2. When a car (or a missile) moves from cell (i,j)(i, j) to cell (i,j+2)(i, j+2), it also passes through cell (i,j+1)(i, j+1).

  3. When a car moves from cell (i,j)(i, j) to cell (i+1,j+2)(i+1, j+2), it also passes through cells (i,j+1)(i,j+1), (i+1,j+1)(i+1, j+1), (i+1,j)(i+1, j) and (i,j+2)(i, j+2). Similarly when it moves to (i1,j+2)(i-1, j+2).

  4. In the other cases it does not pass through intermediate cells.

The exact order in which intermediate cells are visited can be looked up in the map dir2all defined in Utils.cc.

Order of execution of instructions

After the instructions of all players are collected, the following actions take place:

  1. First missiles already shot are moved according to their rules.

  2. Next a random order is determined among the players, and the instructions for their cars are executed following this order. Instructions given by the same player are executed in their original order. Invalid instructions (for example, moving outside the window limits) are ignored. If a car receives more than one instruction, only the first one will be taken into account. Cars that have not received any instruction will move by default, in increasing order of identifier.

  3. After all instructions are executed, all cars that are alive have their gas units decremented by one. After that, any car with 0 gas units is killed.

  4. If appropriate, dead cars and new bonuses (in this order) are regenerated.

Game parameters

A game is determined by following set of parameters. All of them are specified in the input file, but some of them will always have the same value:

@number_players()@: number of players in the game. Fixed to 4.

@number_rounds()@: number of rounds that will be played. Fixed to 300.

@number_rows()@: number of rows of the universe (and of the window). Variable in [15,20][15,20].

@number_universe_columns()@: number of columns of the universe. Variable in [60,100][60,100].

@number_window_columns()@: number of columns of the window. Variable in [30,40][30,40].

@number_cars_per_player()@: number of cars for each player. Fixed to 2.

@number_cars()@: total number of cars. Fixed to 8.

@number_rounds_to_regenerate()@: number of rounds to wait before a car can regenerate. Fixed to 30.

@number_missile_bonuses()@: number of missile bonuses in the game. Variable in [5,30][5,30].

@number_water_bonuses()@: number of water bonuses in the game. Variable in [20,150][20,150].

@number_gas_bonuses()@: number of gas bonuses in the game. Variable in [5,15][5,15].

@bonus_missiles()@: number of extra missiles obtained when consuming a missile bonus. Fixed to 5.

@bonus_gas()@: number of extra gas units obtained when consuming a gas bonus. Fixed to 30.

@water_points()@: number of extra points obtained when consuming a water bonus. Fixed to 10.

@kill_points()@: number of points obtained when killing a car of another player. Fixed to 30.

@initial_gas()@: number of gas units given to a car when it is regenerated. Fixed to 60.

All these parameters can be accessed by the players during the game.

Programming

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 HTML5/Javascript viewer to watch them in a nice animated format. Also, a ”Demo” player is provided to make it easier to start coding your own player.

Running your first match

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 Chromium.

To run your first match, follow the next steps:

  1. Open a console and cd to the directory where you extracted the source code.

  2. If, for example, you are using a 64-bit Linux version, run:

    cp AIDummy.o.Linux64 AIDummy.o

    If you use any other architecture, choose the right object you will find in the directory.

  3. Run make all to build the game and all the players. Note that the Makefile will identify as a player any file matching the expression ”AI*.cc”.

  4. The call to make should create an executable file called Game. This executable allows you to run a match as follows:

    ./Game Demo Demo Demo Demo -s 3424 < default.cnf > default.res

    Here, we are starting a match with 4 instances of the player ”Demo” (included with the source code), with the game configuration defined in ”default.cnf”. The output of this match will be stored in ”default.res”. A random seed of 3424 will be used in this run.

  5. To watch the match, open the viewer (viewer.html) with your browser and load the ”default.res” file.

Use

./Game --help

to see the list of parameters that you can use. Particularly useful is

./Game --list

to show all the available players.

If needed, remember you can run make clean to delete the executable and all object files and start over the build.

Adding your player

To create a player, copy the file AINull.cc (an empty player that is provided as a template) to a new file with the same name format (AIWhatever.cc).

Then, edit the file you just created and change the playername line to your own player name, as follows:

@#define PLAYER_NAME Whatever@

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 @Board@ class in Board.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 via the class @Player@ in Player.hh . The documentation on the available functions can be found in the aforementioned header files of each class. 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 file Utils.hh for useful data structures.

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 available players.

Playing against the Dummy player

To test your strategy against the Dummy player, we provide the object file for it. This way you still will not have the source code of our Dummy, but you will be able to add it as a player and compete against it locally.

To add the Dummy player to the list of registered players, you will have to edit the Makefile file and set the variable DUMMY_OBJ to the appropriate value. Remember that object files contain binary instructions targeting a specific machine, so we cannot provide a single, generic file.

Pro tip: You can ask your friends for the object files of their players and add them to the Makefile too!

Restrictions when submitting your player

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:

Tips