Contents
- Game overview
- Quickstart
- Experiment mechanics (for users)
- Sample battle between random and intelligent players
- Experiment implementation (for developers)
Game overview
Capture the flag is a team game. Alongside Deathmatch, it is one of the most popular types in many FPS games. Usually, two teams fight to capture the other team's flag, and bring it back to their own flag. However, the game can be generalized to as many teams as the user wants to. Points are scored for each flag brought to the own flag.
Quickstart
Even though the lecture of the Experiment mechanics section is strongly advised, you can follow this simple guide to start working with Capture the flag.
First, you have to download the experiment sources and unpack them to the "scripts" directory in your Framsticks installation directory.
The next step is loading the experiment in Framsticks. You can do this by entering parametrs menu (Simulation->Parameters or Ctrl+P) and selecting CTF from the dropdown list. You have to confirm the change by clicking the Apply button.
After the experiment has been loaded, the user can set specific experiment parameters by entering Experiment→Parameters in the parameters tree on the left side, as illustrated below.
The first step is to set the desired number of teams, the number of team members and the minimal distance between flags. After applying the changes, the "Create teams" button needs to be pressed. It creates all required GenePools and Populations, which are all empty, except for an extra GenePool containing the Flag genotype. Clicking the "Initialize experiment" button does the same thing, but the parameters mentioned above are set to default values.
If the user does not want to create their own player genotypes, they can press the "Insert default player" button. It inserts a "smart" player in Team 1 and random players in the other teams. If the user wants to create their own genotypes, they must close the Parameters window after creating teams and prepare the genotypes.
After players' genotypes have been prepared, the last action needs to be performed – clicking the "Fill teams" button. After that, the Population for each team will be filled with one flag and the desired number of players. If the minimal distance between flags is set too high, the flags can not be placed correctly, so you will get a message asking to reset the experiment with lower minimal distance value.
You can also set the amount of points a player is awarded for each action – taking a flag, retrieving it and scoring.
Experiment mechanics (for users)
Each team fighting in this game has their own Population, built from genotypes from their GenePool. Therefore, each team is composed of copies of a different player, but... players from one team can differ from each other as well. If the gene pool consists of more than one genotype, then – when populations are filled with players – genotypes are selected using round-robin method.
Players from each team are placed close to their own flag in the beginning of the fight, and all of them have the same amount of starting energy, which is constant throughout the whole battle. It means that killing a player from another team (frags) or from the own team (friendly fire) is not possible. Flag positions are random with respect to the minimal distance between two flags, set by the experiment parameter.
The main part of the simulation is collisions handling, because all actions that can be done are based on colliding with another creature. To make collision mechanics simpler, first player of each team (creature #0 in each population) is the team flag, which is also a creature built using a genotype from the predefined gene pool. We can distinguish 3 main collision types:
- player vs. player
- player vs. flag
- flag vs. flag
As the first two happen quite often and are essential for the game, the third can happen really seldom and doesn't influence the game. Let's take a closer look at the first two collision types.
- Player vs. player collision. When two players collide, there are two situations to consider – the players are from the same team or, the players are from different teams. In the first case, no further action is taken. When the players are from different teams though, a check is done: if creatureA is holding flag of creatureB, the flag is returned to its home place. Therefore, if two creatures collide holding each other's flags, a "suicide move" is performed – each flag is returned to its home place.
- Player vs. flag collision. In this case, more situations are possible. The first one is when a player not carrying any flag collides with other team's flag which is not yet taken – so the flag is taken by the player. When a player collides with his own team's flag that is taken – the flag is immediately returned to its home place and the player scores points for retrieving the flag. The last and the most important situation is when a player carrying other team's flag collides with his own flag: the player scores the flag, which is immediately returned to its home place.
Another important factor is sending and receiving signals. Each flag is an emitter of a signal, which can be received by a creature equipped with a specially designed neuron. The power of each signal is sufficient to reach every point of the world; each creature can receive information on every flag in any moment of time. Using the power of the signal, users should design creatures to go to a specific flag. Using signal-receiving neurons allows creatures to learn which team the flag belongs to and whether it is taken or not.
Scoring in this game is slightly different than in a standard CTF battle. Each player scores their own points. The global team score can be calculated by summing scores of each player in the team. Points can also be awarded for other events: taking opponent's flag, retrieving own flag, and – as in standard version – bringing the opponent's flag to the own flag. The amount of points for each event can be adjusted using experiment parameters.
Sample battle between random and intelligent players
To illustrate how this experiment works, a simple battle has been performed. It employed two teams of four players each. Team number 1 consisted of default "food finders", so in the CTF context they where random players. Team number 2 consisted of modified food finders – instead of the standard smell sensors, specially constructed sensors were plugged in. These sensors provided signal strength of the opponents' flag when both flags were at their own place, and the own flag signal strength when either flag was taken. Therefore, players from Team 2 were acting in a defensive way – when their flag was taken, they abandoned all other activities and tried to retrieve it. Note that both types of creatures have the same agility and speed, because they only differ in the sensors they use.
Let's see what are the results of this battle.
Overall score
We can see that the "smart" team crushed the random team. They scored nearly 4 times more points. Let's see what kind of actions gave Team2 such a big advantage.
The number of taken flags
Team1 took the opponent's flag nearly 10 times less than their enemies. It was only due to random collisions and luck, while the guys from Team2 were actually aiming for the opponent's flag.
The number of retrieved flags
This is the only type of action where Team1 outscored Team2. It is simply due to the fact that Team2 took Team1's flag more often than the other way round. Therefore, Team1, which was generally circling around their own flag, had more opportunities to retrieve their taken flag. This graph is very similar to the previous graph that showed the number of times a flag was taken. The only difference is the fact that not all flags taken by Team2 were retrieved – some of them were brought to Team2's flag and scored.
The number of scored flags
This graph explains the dominance of Team2 over Team1. They scored 7 flags, which – with default points settings – gave them 350 points. Team1 could not score a single flag, because they were always stopped by the defensive Team2. It was the main factor that lead to the victory of Team2.
Experiment implementation (for developers)
As mentioned before, each team has their own GenePool containing genotypes of possible players. Each team has their own Population as well. The Population contains fighting players created from genotypes, and a flag.
During filling teams with players, flag positions are set randomly and are stored in a global array. Holding initial flag positions is necessary to place them back on their spots when they are retrieved or scored.
Collisions are implemented using the on{Team}CrCollision mechanism and are uniform for each Population. In this function, the collision logic described in previous paragraph is implemented.
To keep track of the flag holder and points of each creature, custom fields in Creature.data dictionary are used. For each player, the fields are:
- data->holding – whether the creature holds the flag (value of 1) or not (value of 0).
- data->flag – the flag object if the creature has a flag, or 0 otherwise.
- data->score – player score.
- data->taken – whether the flag is taken (value of 1) or not (value of 0).
- data->owner – the player object that holds the flag when it is taken, or 0 otherwise.
- data->cooldown – "cooldown" of a flag; when the flag is retrieved, it is set to 1000 simulation steps, so the flag cannot be taken again until cooldown reaches 0.
The score is kept for each player, and is the default creature fitness function. Also, the score for each team and counters of each action performed (taking, retrieval and scoring the flag) are stored in global arrays.
In the onStep() function two actions are performed. First, for each flag it is checked whether the flag is taken. If it is true, then the flag is moved above the player that holds it. Also, for each flag, if the cooldown is greater than 0, it is decremented by 1.
As an example of how a flag can be sensed, a simple neuron ("FlagNeuron") has been created. Playes will most probably want to create neurons that have similar function, but are more sophisticated. The FlagNeuron listens for a single signal on the "Flag" channel. The flavor it listens to is the FlagNeuron's parameter. The flavor is the number of the team (starting from 0) when the flag is not taken, or the number of the team increased by 100 when the flag is taken. For example, if we wanted to sense the untaken Team2's flag, we would use [Flag,team:1]
. If we wanted to sense Team4's flag which is taken, then we would use [Flag,team:103]
.
"Capture the flag" was developed by Artur Jaworski during the Bio-inspired computing and systems course at Poznan University of Technology. If you have any comments, suggestions or ideas, please contact the author or the mentor.