This competition gives you an opportunity to test your programming skills, by creating an artificial intelligence (strategy) playing a game in a special world (you can learn about details of the CodeCraft 2020 world in later sections). In each game you are to compete against other players' strategies. Your team's goal is to gain more score than your opponents.
The tournament is held in several stages (Round 1, Round 2 and Finals) preceded by a qualification in the Sandbox. Sandbox is a competition that takes place throughout the championship. Each participant has a certain rating value — an indicator of how successful their strategy is involved in games within each stage.
The initial value of the rating in the Sandbox is 1200
. At the end of the game this value can both be increased and decreased. At the same time victory
over a weak (with a low rating) opponent gives a small increase, also the defeat from a strong opponent slightly decreases your
rating. Over time the rating in the Sandbox becomes more and more inert, which makes it possible to decrease the impact of random long series of victories or
defeats on the participant's place, but at the same time makes it difficult to change their position with a significant improvement in strategy. To cancel such effect
the participant can reset the variability of the rating to the initial state when sending a new strategy, including the corresponding
option. If the new strategy is adopted, the rating system of the participant will fall dramatically after the next game in the Sandbox, however,
further participation in games will quickly recover and even become higher if your strategy has really become more effective. It is not recommended
to use this option with minor, incremental improvements to your strategy, as well as in cases where a new strategy
insufficiently tested and the effect of changes in it is not known reliably.
The initial value of the rating at each main stage of the tournament is 0
. For each game the participant receives a certain number of rating points
depending on the occupied place (a system similar to that used in the championship "Formula-1"). If two or more participants share
some place, then the total number of rating points for this place and for the following number_of_such_members - 1
of places is shared
equally among these participants. For example, if two participants share the first place, then each of them will receive half of the rating points number
for the first and second places. When sharing rounding always takes place in a smaller direction. More detailed information about the stages of the tournament will be
provided in the announcements on the project website.
First all participants can participate only in the games that take place in the Sandbox. Players can send their strategies to the Sandbox, and
the last one taken from them is taken by the system for participation in qualifying games. Each player participates in approximately one qualifying
game for an hour. The jury reserves the right to change this interval based on the throughput of the testing system, but for
the majority of participants it remains constant. There are a number of criteria by which the interval of participation in qualifying games
can be increased for a specific player. For every N-th full week that has elapsed since the player sent the last strategy, the interval
of participation for this player is increased by N basic test intervals. Only the strategies adopted by the system are taken into account. An additional penalty which is equal to 20%
from the basic testing interval is charged in the Sandbox for each strategy "crash" in 10
last games. The player’s participation interval in the Sandbox can not become bigger than a day.
Games in the Sandbox are held according to a set of rules corresponding to the rules of a random stage among the passed ones and the next (current) one. At the same time, the closer the rating value of the two players rating within the Sandbox, the more likely that they will be in one game. The Sandbox starts before the start of the first stage of the tournament and ends after some time after the final stage (see the schedule
of stages to clarify the details). In addition, the Sandbox is frozen during the stages of the tournament. Following the results of the games in the Sandbox
there is a selection for participation in Round 1, which will involve no more than 1080
participants (if there is less participants, maximal number divisible by 4
) with the highest rating at the beginning of this stage of the tournament
(if the rating is equal, priority is given to the player who previously sent the latest version of their strategy), as well as an additional selection to
the next stages of the tournament, including the Finals.
Round 1, as all further stages, consists of two parts, between which there will be a short break (with the renewal of the Sandbox work), which
allows to improve its strategy. The last strategy sent by the player before the beginning of this part is selected for the games in each part.
Games are conducted in waves. In each wave, each player participates exactly in one game. The number of waves in each part is determined by
the capabilities of the testing system. 300
highest rated participants will proceed to Round 2. Also in Round 2 there will be an additional selection of 60
participants with the highest rating in the Sandbox (at the moment of Round 2 beginning) among those who did not passed according to the results of Round 1.
According to the results of Round 2 the 50
best strategies will reach the Finals. Also in the Finals there will be an additional selection of 10
participants with
the highest rating in the Sandbox (at the beginning of the Finals) from those who did not go through the main tournament.
The system of holding the Finals has its own peculiarities. The stage is still divided into two parts, but they will no longer consist of waves. In each part of the stage, games will be played between all pairs of Finals participants. If the time and capabilities of the testing system permit, the operation will be repeated.
All finalists are ranked according to the non-increase in the rating after the end of the Finals. If the ratings are equal, a higher place is taken by that finalist, whose strategy, which was part of the Final, was sent out earlier. Prizes for the Final are distributed based on the occupied place after this ordering.
After the completion of the Sandbox, all its participants, except for the Finals winners, are ranked according to the non-increase in the rating. If the ratings are equal a higher place is taken by the participant who sent the latest version of their strategy earlier. Prizes for the Sandbox are distributed on the basis of occupied place after this ordering.
Time in the game is discrete and is measured in "ticks". At the beginning of each tick, the game simulator transmits the world state data to the participants' strategies, then receives actions from them and updates the state of the world in accordance with these actions and the rules of the game. Then the process is repeated again for next tick with the updated state. There is a maximum duration of the game, but it can also be terminated prematurely if all strategies have "crashed".
The "crashed" strategy can no longer control player's behavior. The strategy is considered "crashed" in the following cases:
-
The process in which the strategy is started has unexpectedly terminated, or an error has occurred in the protocol of interaction between the strategy and game server.
-
The strategy exceeded one of the time constraints assigned to it. There is a time limit for strategy to reply with an action for each tick -
1
second of real time, as well as a time limit for the whole game -40
seconds of cpu time. -
The strategy exceeded the memory limit -
256
MB.
The game of CodeCraft 2020 is a strategy where you will have to control a number of units, gather resources, build your settlement and attack your enemies.
Your goal is to gain more score than your opponents. The game ends either when the max tick count has been reached or when there is only one (or zero) players left. When playing 1v1 (Finals), when only one player is left, he gets additional score enough to win the game.
The game is played on a rectangular grid, divided into tiles. All game entities have square shape and are located at some integer coordinates. When calculating distance, we count the number of tiles that need to be traversed to reach destination, going to a neighboring tile at one time (Manhattan distance).
Entities' behavior is defined by their properties.
One of the most important properties defines the size of an entity. All entities in the game have square shape, with a side length equal to the defined value.
Some entities can move (these entities are called units). Moving entities always have a size of 1
. They can move to a neighboring tile in one tick, if that tile is not occupied by some other entity.
Some entities can attack, and all entities have health and can be destroyed. If entity's health gets below or equal to zero, it is removed from the game. Attacking entities have a limited attacking range, which is a maximum distance to the target for performing the attack. Each tick an entity is attacking, it subtracts a certain amount of health points from the target.
Also, some entities can repair other entities. Only adjacent entities can be repaired. Repairing is restoring a specified amount of health points in on tick. When repairing, target's health can not go above its maximum health, specified in its properties. Only alive entities (with positive health) can be repaired.
Some of the attacking entities can also collect resources from the target. For each health point of damage, a fixed amount of resource (specified in target entity's properties) is added to the attacker's owning player.
Gathered resources can be used to build new entities. Some entity types can do that. New entity's type is limited by capabilities of the builder, listed in its properties. To build a new entity, you have to spend a specified amount of resources. For units (movable entities), the exact amount of resources is equal to the value specified in properties of this unit's type, plus current amount of player's units of this type. For other entities, the cost is equal to the initial cost. You should also select a location not occupied by other entities, and located adjacent to the builder. Newly built entities have initial health equal either to entity's maximum health, or to a specific value, as specified in builder entity's properties.
When an entity is just built, it is inactive at first, meaning it can not perform any actions. To activate an entity, it has to reach its maximum health first. So, if an entity was built not with full health, it needs to be repaired first.
Also, there is one more restriction to building new entities. Besides resources, there is another parameter called "population". Some entities provide population, while others use it. In order to build a new entity, the sum of population provided by all current player's active entities should be greater than or equal to the sum of population used by all current player's entities, including newly built one.
The last property of an entity is its sight range. If fog of war is enabled, your strategy can only see those entities that are located no further than distance specified from some entity that is controlled by you.
There is a fixed number of entity types in the game, and entities of same type have same properties. Here is a full list of entity types:
-
Resource. This is the only entity type that is not controlled by any player. It has a size of
1
and should be attacked by builder units in order to be gathered. -
Builder unit. Its primary purpose is to gather resources and construct buildings.
-
Melee unit. Basic damage dealing unit with an attack range of
1
. -
Ranged unit. Deals less damage than melee unit, but has bigger attack range.
-
Builder/Melee/Ranged bases. These are buildings used to buy new units of corresponding type. Can be built by a builder unit.
-
Wall. A small building that can be used to block path for enemy.
-
House. A building that provides population.
-
Turret. A building that can attack enemies. Since it can not move, its best for defence.
Each tick your strategy is going to be asked for actions of your entities. If you do not specify action for an entity, it continues to perform previously set action.
Entity's action consists of attack, build, repair and move actions, which are prioritized in this order. So, if you specify multiple actions, then only the first one that is possible to do this current tick is actually performed.
Attack action can specify a specific entity to attack, or perform an auto attack. When using auto attack, you can also specify a distance where your unit should try to pathfind to try and find an enemy to attack.
Repair action can only specify an entity to repair.
For build action you would need to specify a type of entity you want to build as well as position. Position of an entity is it's corner with minimal coordinates.
For move action target position needs to be specified. A unit will try to pathfind to this position. You can further control pathfinding by specifying whether to find closest position. In case you do not want this, unit will not move if the path is not found. You may also specify whether to try to find such a route which involves breaking through other entities, attacking and destroying them on the way. Such algorithm will only consider to attack entities not controlled by you.
When pathfinding is needed to be performed by the game server, a simple A* algorithm is used, with limited number of nodes to be visited.
Each game tick, first pathfinding is being performed for moving entities to determine their potential next position. If entity's move action target is adjacent to the entity, no pathfinding is performed, and instead this position is being remembered. Next, all attack actions are performed for active entities in random order. If no valid target is found, but the position found in previous step contains an enemy, this enemy is being attacked. If target's health was positive and becomes zero, target is considered destroyed and score is added to the attacker (but target is not removed from the game just yet). Next, build actions are performed (if an entity has performed an attack action this tick, build action will not be performed). Then repair actions are performed in same way. Only entities with positive health are being repaired. And last, movement is being performed. Movement is performed in steps. In each step, entities are trying to move to their next position as determined in the pathfinding stage. If several entities are trying to move to the same location, a random one is chosen. If no entities can be moved, movement phase is finished.
In the end, entities with zero health are removed from the game, and entities with full health become active.
In Round 1 you are to learn the rules of the game. To simplify things, there will be no fog of war in this stage. Also, you will be given a base for each unit type in the beginning of the game, so you can start gathering resources and attacking your opponent right away. You can still experiment with buildings to prepare for next stages.
In Round 2 you have to learn building. In the beginning you will only have builders. You will need to build bases for other types of units. Also, fog of war will now be enabled so you will have to do some exploration before confronting the enemy. The task is further complicated that after summarizing the Round 1, the part of the weak strategies will be eliminated and you will have to confront stronger opponents.
Finals is the most important stage. After the selection, held following the results of the first two stages, the strongest participants will be remained. The games in the finals are going to be 1v1. Besides that, if there is only one player left in the game, they are given enough score points to win the game.
You have an option to run simple test games locally on your computer. To do so, download the app. Use of the app will allow you to test your strategy in an environment similar to the environment of a testing game on the site, but without any restrictions on the number of games created.
When you start the app, you will see a config screen. You can select strategies participating in the game and configure some game settings. If you want to test your strategy, select TCP player, and then start your strategy. By default port 31001
is used. After successful connection, you will be able to start the game.
If you want to change the port, for example to connect multiple strategies, run your language package specifying host and port. For example, ./aicup2020 localhost 31002
.
After all players have connected, instead of starting new game, you may also start the game using saved game state, or repeat previously saved game. While repeating a game, your strategy will receive data from the server but all the actions will be ignored.
Controls in the app:
- RMB / Shift-LMB - move camera
- MMB / Ctrl-LMB - rotate camera
- V - change visualization mode.
- Ctrl-S - save current game to a file (you can use replay feature)
- Ctrl-E - save current game state (you can load game state and start game from it)
- P - pause/play
- Left/Right - move time tick by tick (when paused)
You can save the config to a file, and then run the app with --config <file>
option, bypassing this config screen. Other available options are listed when using --help
option.## Entity properties values
Here you can see the value of entity properties:
{
House: (
size: 3,
build_score: 50,
destroy_score: 500,
can_move: false,
population_provide: 5,
population_use: 0,
max_health: 50,
initial_cost: 50,
sight_range: 5,
resource_per_health: 0,
build: None,
attack: None,
repair: None,
),
RangedUnit: (
size: 1,
build_score: 30,
destroy_score: 300,
can_move: true,
population_provide: 0,
population_use: 1,
max_health: 10,
initial_cost: 30,
sight_range: 10,
resource_per_health: 0,
build: None,
attack: Some((
range: 5,
damage: 5,
collect_resource: false,
)),
repair: None,
),
BuilderUnit: (
size: 1,
build_score: 10,
destroy_score: 100,
can_move: true,
population_provide: 0,
population_use: 1,
max_health: 10,
initial_cost: 10,
sight_range: 10,
resource_per_health: 0,
build: Some((
options: [
House,
Wall,
BuilderBase,
MeleeBase,
RangedBase,
Turret,
],
init_health: Some(5),
)),
attack: Some((
range: 1,
damage: 1,
collect_resource: true,
)),
repair: Some((
valid_targets: [
House,
Wall,
BuilderUnit,
MeleeUnit,
RangedUnit,
BuilderBase,
MeleeBase,
RangedBase,
Turret,
],
power: 1,
)),
),
MeleeUnit: (
size: 1,
build_score: 20,
destroy_score: 200,
can_move: true,
population_provide: 0,
population_use: 1,
max_health: 50,
initial_cost: 20,
sight_range: 10,
resource_per_health: 0,
build: None,
attack: Some((
range: 1,
damage: 5,
collect_resource: false,
)),
repair: None,
),
Wall: (
size: 1,
build_score: 10,
destroy_score: 10,
can_move: false,
population_provide: 0,
population_use: 0,
max_health: 50,
initial_cost: 10,
sight_range: 2,
resource_per_health: 0,
build: None,
attack: None,
repair: None,
),
Resource: (
size: 1,
build_score: 0,
destroy_score: 0,
can_move: false,
population_provide: 0,
population_use: 0,
max_health: 30,
initial_cost: 0,
sight_range: 0,
resource_per_health: 1,
build: None,
attack: None,
repair: None,
),
Turret: (
size: 2,
build_score: 50,
destroy_score: 500,
can_move: false,
population_provide: 0,
population_use: 0,
max_health: 100,
initial_cost: 50,
sight_range: 10,
resource_per_health: 0,
build: None,
attack: Some((
range: 5,
damage: 5,
collect_resource: false,
)),
repair: None,
),
BuilderBase: (
size: 5,
build_score: 500,
destroy_score: 5000,
can_move: false,
population_provide: 5,
population_use: 0,
max_health: 300,
initial_cost: 500,
sight_range: 5,
resource_per_health: 0,
build: Some((
options: [
BuilderUnit,
],
init_health: None,
)),
attack: None,
repair: None,
),
RangedBase: (
size: 5,
build_score: 500,
destroy_score: 5000,
can_move: false,
population_provide: 5,
population_use: 0,
max_health: 300,
initial_cost: 500,
sight_range: 5,
resource_per_health: 0,
build: Some((
options: [
RangedUnit,
],
init_health: None,
)),
attack: None,
repair: None,
),
MeleeBase: (
size: 5,
build_score: 500,
destroy_score: 5000,
can_move: false,
population_provide: 5,
population_use: 0,
max_health: 300,
initial_cost: 500,
sight_range: 5,
resource_per_health: 0,
build: Some((
options: [
MeleeUnit,
],
init_health: None,
)),
attack: None,
repair: None,
),
}
In language pack for your programming language you can find file named MyStrategy.<ext>
/my_strategy.<ext>
.
This file contains class MyStrategy
with get_action
method, where your strategy's logic should be implemented.
This method will be called each tick.
The method takes following arguments:
- Player view — all the information you have about current game's state,
- Debug interface — this object allows you to do send debug commands to the app and receive debug state from inside your strategy code. Note that this is unavailable when testing your strategy on the server, or using the app in batch mode. This is for local debugging only.
The method should return the action you desire to perform this tick.
For debugging purposes, there is also another method — debug_update
, that has same parameters, and is called continiously while the app is running (not in batch mode), if the client is waiting for the next tick. There will always be at least one debug update between ticks.
In this section, some fields may be absent (denoted as Option<type>
).
The way this is implemented depends on the language used.
If possible, a dedicated optional (nullable) type would be used,
otherwise other methods may be used (like a nullable pointer type).
Some objects may take one of several forms. The way it is implemented depends on the language. If possible, a dedicated sum (algebraic) data type is used, otherwise other methods may be used (like variants being classes inherited from abstract base class).
2 dimensional vector.
Fields:
x
:float32
-x
coordinate of the vectory
:float32
-y
coordinate of the vector
RGBA Color
Fields:
r
:float32
- Red componentg
:float32
- Green componentb
:float32
- Blue componenta
:float32
- Alpha (opacity) component
Vertex for debug rendering
Fields:
world_pos
:Option<Vec2Float32>
- Position in world coordinates (if none, screen position (0, 0) is used)screen_offset
:Vec2Float32
- Additional offset in screen coordinatescolor
:Color
- Color to use
Primitive type for debug rendering
Variants:
Lines
- Lines, number of vertices should be divisible by 2Triangles
- Triangles, number of vertices should be divisible by 3
Debug data can be drawn in the app
One of:
-
Log
- Log some textFields:
text
:string
- Text to show
-
Primitives
- Draw primitivesFields:
vertices
:[ColoredVertex]
- Verticesprimitive_type
:PrimitiveType
- Primitive type
-
PlacedText
- Draw textFields:
vertex
:ColoredVertex
- Vertex to determine text position and colortext
:string
- Textalignment
:float32
- Text alignment (0 means left, 0.5 means center, 1 means right)size
:float32
- Font size in pixels
Debug commands that can be sent while debugging with the app
One of:
-
Add
- Add debug data to current tickFields:
data
:DebugData
- Data to add
-
Clear
- Clear current tick's debug dataNo fields
-
SetAutoFlush
- Enable/disable auto performing of commandsFields:
enable
:boolean
- Enable/disable autoflush
-
Flush
- Perform all previously sent commandsNo fields
2 dimensional vector.
Fields:
x
:int32
-x
coordinate of the vectory
:int32
-y
coordinate of the vector
Move action
Fields:
target
:Vec2Int32
- Target positionfind_closest_position
:boolean
- Whether to try find closest position, if path to target is not foundbreak_through
:boolean
- Whether to destroy other entities on the way
Entity type
Variants:
Wall
- Wall, can be used to prevent enemy from moving throughHouse
- House, used to increase populationBuilderBase
- Base for recruiting new builder unitsBuilderUnit
- Builder unit can build buildingsMeleeBase
- Base for recruiting new melee unitsMeleeUnit
- Melee unitRangedBase
- Base for recruiting new ranged unitsRangedUnit
- Ranged unitResource
- Resource can be harvestedTurret
- Ranged attacking building
Build action
Fields:
entity_type
:EntityType
- Type of an entity to buildposition
:Vec2Int32
- Desired position of new entity
Auto attack options
Fields:
pathfind_range
:int32
- Maximum distance to pathfindvalid_targets
:[EntityType]
- List of target entity types to try to attack. If empty, all types but resource are considered
Attack action
Fields:
target
:Option<int32>
- If specified, target entity's IDauto_attack
:Option<AutoAttack>
- If specified, configures auto attacking
Repair action
Fields:
target
:int32
- Target entity's ID
Entity's action
Fields:
move_action
:Option<MoveAction>
- Move actionbuild_action
:Option<BuildAction>
- Build actionattack_action
:Option<AttackAction>
- Attack actionrepair_action
:Option<RepairAction>
- Repair action
Player's action
Fields:
entity_actions
:Map<int32 -> EntityAction>
- New actions for entities. If entity does not get new action, if will continue to perform previously set one
Message sent from client
One of:
-
DebugMessage
- Ask app to perform new debug commandFields:
command
:DebugCommand
- Command to perform
-
ActionMessage
- Reply for ServerMessage::GetActionFields:
action
:Action
- Player's action
-
DebugUpdateDone
- Signifies finish of the debug updateNo fields
-
RequestDebugState
- Request debug state from the appNo fields
Entity's build properties
Fields:
options
:[EntityType]
- Valid new entity typesinit_health
:Option<int32>
- Initial health of new entity. If absent, it will have full health
Entity's attack properties
Fields:
attack_range
:int32
- Maximum attack rangedamage
:int32
- Damage dealt in one tickcollect_resource
:boolean
- If true, dealing damage will collect resource from target
Entity's repair properties
Fields:
valid_targets
:[EntityType]
- Valid target entity typespower
:int32
- Health restored in one tick
Entity properties
Fields:
size
:int32
- Size. Entity has a form of a square with side of this lengthbuild_score
:int32
- Score for building this entitydestroy_score
:int32
- Score for destroying this entitycan_move
:boolean
- Whether this entity can movepopulation_provide
:int32
- Number of population points this entity provides, if activepopulation_use
:int32
- Number of population points this entity usesmax_health
:int32
- Maximum health pointsinitial_cost
:int32
- Cost to build this first entity of this type. If this is a unit (entity can move), the cost is increased by 1 for each existing unit of this typesight_range
:int32
- If fog of war is enabled, maximum distance at which other entities are considered visibleresource_per_health
:int32
- Amount of resource added to enemy able to collect resource on dealing damage for 1 health pointbuild
:Option<BuildProperties>
- Build properties, if entity can buildattack
:Option<AttackProperties>
- Attack properties, if entity can attackrepair
:Option<RepairProperties>
- Repair properties, if entity can repair
Player (strategy, client)
Fields:
id
:int32
- Player's IDscore
:int32
- Current scoreresource
:int32
- Current amount of resource
Game entity
Fields:
id
:int32
- Entity's ID. Unique for each entityplayer_id
:Option<int32>
- Entity's owner player ID, if owned by a playerentity_type
:EntityType
- Entity's typeposition
:Vec2Int32
- Entity's position (corner with minimal coordinates)health
:int32
- Current healthactive
:boolean
- If entity is active, it can perform actions
Information available to the player
Fields:
my_id
:int32
- Your player's IDmap_size
:int32
- Size of the mapfog_of_war
:boolean
- Whether fog of war is enabledentity_properties
:Map<EntityType -> EntityProperties>
- Entity properties for each entity typemax_tick_count
:int32
- Max tick count for the gamemax_pathfind_nodes
:int32
- Max pathfind nodes when performing pathfinding in the game simulatorcurrent_tick
:int32
- Current tickplayers
:[Player]
- List of playersentities
:[Entity]
- List of entities
Message sent from server
One of:
-
GetAction
- Get action for next tickFields:
player_view
:PlayerView
- Player's viewdebug_available
:boolean
- Whether app is running with debug interface available
-
Finish
- Signifies end of the gameNo fields
-
DebugUpdate
- Debug updateFields:
player_view
:PlayerView
- Player's view
Camera used for rendering
Fields:
center
:Vec2Float32
- Center point at which camera is lookingrotation
:float32
- Rotation angleattack
:float32
- Attack angledistance
:float32
- Distance to centerperspective
:boolean
- Whether perspective is applied
Debug state to be received from the app
Fields:
window_size
:Vec2Int32
- Size of the drawing canvasmouse_pos_window
:Vec2Float32
- Mouse position in window coordinatesmouse_pos_world
:Vec2Float32
- Mouse position in world coordinatespressed_keys
:[string]
- Currently pressed keyscamera
:Camera
- Current camera used for renderingplayer_index
:int32
- Your player's index