-
Notifications
You must be signed in to change notification settings - Fork 11
Berlin
Berlin is a web-based electronic turn-by-turn multiplayer artificial intelligence strategy board game. Which means:
- It is web-based.
- It is electronic.
- It is turn-based.
- It is multi-player.
- It is played by AIs.
- It is a strategy game.
- It is kind of a board game.
The goal of Berlin is to move soldiers from nodes to nodes through paths on a map to attack the enemy and secure victory points.
- Map: the map is a set of nodes linked together by paths. In other terms: a graph.
- Nodes: the nodes are where the soldiers are after each turn. They are key to winning the game.
- Paths: A path is a link between one node and another. It can be directed (one-way) or undirected (bidirectionnal). Each turn, a soldier can move across a single path.
- Soldiers: the only unit of the game. This is what your AI controls: it move soldiers from nodes to nodes.
Berlin is turn-based, which means that from a game state A, players make moves and then we resolve those moves to generate the game state B. Specifically, this means:
- A game has a determined number of turns.
- Each turn:
- Players AIs move soldiers from nodes to nodes.
- Combats are resolved for nodes where more than one player is present.
- Ownerships are resolved for nodes where more than one player is present.
- Soldiers are spawned/killed on certain nodes.
- After the last turn, victory points are assigned.
Movements are the only actions that can be performed by your AI. A movement consist of:
- The from node.
- The to node.
- The number of soldiers to move.
Check out the getting started section for more info.
Combats occur after movements. At the end of movements, it is likely (unless your AI is a coward) that more than one player occupy a node, thus a combat occur.
Combats occur in a simple and predictable manner: all players lose one ally soldier for one enemy soldier (i.e. 1:1 ratio) until a single player is left or all soldiers are lost.
Given the following scenario for a combat:
- Player #1 owns the node
- Player #1 has 3 soldiers
- Player #2 has 4 soldiers
- Player #3 has 2 soldiers
Player #2 will conquer the node and have 1 soldier left.
Given the following scenario for a combat:
- Player #1 owns the node
- Player #1 has 3 soldiers
- Player #2 has 3 soldiers
The combat will end in a draw. Player #1 will maintain ownership of the node.
Given the following scenario for a combat:
- No player owns the node
- Player #1 has 1 soldier
That was a tricky scenario. No combat will occur. Player #1 will gain ownership of the node.
Ownership resolution is performed after combats. At the end of combats, nodes will have zero or one player standing (and this player can have 0-N soldiers on the node).
So, on a given occupied node, there is an ownership transfer when the player at the end of the turn differ from the player at the beginning of the turn.
Given the following scenario for a combat on a node:
- Player #1 owns the node
- Player #1 has 1 soldier
- Player #2 has 2 soldier
Player #2 will conquer the node and have 1 soldier left. So, at the end of the turn, there will be an ownership transfer from player #1 to player #2. Thus, Player #2 will own the node.
Spawning is performed after ownership resolution. Some nodes are worth a certain number of soldiers per turn (positive or negative). On a given node with this property, this number of soldiers is added/removed from the current number of soldiers of the owner.
Given the following scenario for a node:
- Player #1 owns the node with 2 soldiers
- The node give +3 soldiers per turn
Player #1 will have 5 soldiers at the end of the turn.
Given the following scenario for a node:
- Player #1 owns the node with 1 soldier
- The node give -2 soldiers per turn
Player #1 will have 0 soldiers at the end of the turn. Yep, you can't have -1 soldier :)
Check out the anatomy of a request section for more information.
Nodes can be worth points at the end of the game (positive or negative). The total score of a game is solely based on victory points (so NOT based on remaining soldiers, NOT based on amount of controlled nodes, NOT on kills count...).
Given the following scenario at the end of the last turn:
- Player #1 owns a node that gives 1 victory point.
- Player #1 owns a node that gives -1 victory point.
- Player #2 owns a node that gives 3 victory point.
- Player #2 owns a node that gives -2 victory point.
Player #2 wins the game with 1 victory point against player #1 who have 0 victory points.
Check out the anatomy of a request section for more information.
To start playing Berlin you must:
- Write an AI.
- Deploy it publicly (i.e. Berlin must be able to send
POST
requests to it). - Register it on Berlin.
- Start a game!
The hard part is to write your AI. The best part is that you can write it in any language that you want as long as:
- You provide a public URL
- Berlin can send HTTP FORM POST requests with 4 parameters encoded in JSON to that URL
- Your AI can respond with JSON responses to those requests
Berlin communicates with your AI using POST
requests. Your AI reply with a list of the movements it wants to perform on each turn.
Check out the anatomy of a request section to see the requests sent to your AI.
Checkout the anatomy of a response section to see how your AI must respond to those requests.
- At the beginning of a game, Berlin send a
POST
request with the JSON fieldaction
set togame_start
to each AI to make sure they are online and ready to play. AIs have 30 seconds to boot and reply, after which they are considered offline. - For each turn of the game
- Berlin issues a
POST
request to each AI with the JSON fieldaction
set toturn
and the current game state. - Berlin waits for each AI to respond with a list of moves to perform.
- On the Berlin server, the turn is resolved (legal moves applied, combats/ownership/spawns resolved, etc.) and a new game state is generated.
- At the end of the game, Berlin issues a
POST
request to each AI with the JSON fieldaction
set togame_over
. Your AI can use this call to clean up data retained for the game.
All requests contain all the information your AI needs to perform a turn:
- Type of action
- Map information
- Current state of the game
This allows the implementation of stateless AIs.
The request contains 4 parameters: action
, infos
, map
, state
The action
being performed.
It can be game_start, turn, game_over or ping.
"action": "game_start"
The info
section contains the generic informations about the current game.
"infos": {
"game_id": "7c7905c6-2423-4a91-b5e7-44ff10cddd5d", // Unique ID of the game
"current_turn": null, // The current turn number
"maximum_number_of_turns": 10, // After this number of turns, the game ends
"number_of_players": 2, // Total number of AIs in the game
"time_limit_per_turn": 5000, // Time allowed before the turn times out
"directed": false, // Indicated that the paths are one-way only
"player_id": 1 // The ID of your AI for this game
}
The map
section describes the map, nodes and paths between the nodes.
"map": {
// Different node types have different attributes to take into account in your strategies.
"types": [
{"name": "node", "points": 0, "soldiers_per_turn": 0},
{"name": "city", "points": 1, "soldiers_per_turn": 1}
],
// Lists all nodes by their types.
"nodes": [
{"id": 1, "type": "city"},
{"id": 2, "type": "node"},
{"id": 3, "type": "node"},
{"id": 4, "type": "city"}
],
// Lists the paths between nodes.
// If the map is directed, bidirectional paths will include 2 entries.
// For example: {"from": 1, "to": 2} and {"from": 2, "to": 1}.
"paths": [
{"from": 1, "to": 2},
{"from": 2, "to": 3},
{"from": 3, "to": 4},
{"from": 1, "to": 3},
{"from": 1, "to": 4},
{"from": 2, "to": 4}
]
}
The state
section describes the current state of the game.
"state": [
// Ex: Node #1 has 24 soldiers and belongs to player #0.
{"node_id": 1, "player_id": 0, "number_of_soldiers": 24},
// Ex: Node #2 and Node #3 are not owned yet.
{"node_id": 2, "player_id": null, "number_of_soldiers": 0},
{"node_id": 3, "player_id": null, "number_of_soldiers": 0},
// Ex: Node #4 has 24 soldiers and belongs to player #1 (you, see "infos" section).
{"node_id": 4, "player_id": 1, "number_of_soldiers": 24}
]
During each request, the AI is given the opportunity to perform moves. To do so, it must respond by an array of movements.
[
// Ex: Move 2 soldiers from Node #1 to Node #2
{"from": 1, "to": 2, "number_of_soldiers": 2},
// Ex: Move 4 soldiers from Node #1 to Node #3
{"from": 1, "to": 3, "number_of_soldiers": 4}
]
If you think this documentation is not clear (read: suck), open an issue / submit a pull request!