Skip to content
Guillaume Malette edited this page Nov 9, 2013 · 23 revisions

Basic concepts

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.

Goal

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.

Objects

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

Turn-based

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:
    1. Players AIs move soldiers from nodes to nodes.
    2. Combats are resolved for nodes where more than one player is present.
    3. Ownerships are resolved for nodes where more than one player is present.
    4. Soldiers are spawned/killed on certain nodes.
  • After the last turn, victory points are assigned.

Moving

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.

Combat resolution

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

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

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.

Victory points

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.

Getting started

To start playing Berlin you must:

  1. Write an AI.
  2. Deploy it publicly (i.e. Berlin must be able to send POST requests to it).
  3. Register it on Berlin.
  4. 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.

Game flow

  1. At the beginning of a game, Berlin send a POST request with the JSON field action set to game_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.
  2. For each turn of the game
  3. Berlin issues a POST request to each AI with the JSON field action set to turn and the current game state.
  4. Berlin waits for each AI to respond with a list of moves to perform.
  5. On the Berlin server, the turn is resolved (legal moves applied, combats/ownership/spawns resolved, etc.) and a new game state is generated.
  6. At the end of the game, Berlin issues a POST request to each AI with the JSON field action set to game_over. Your AI can use this call to clean up data retained for the game.

Anatomy of a Request

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

Action

The action being performed.

It can be game_start, turn, game_over or ping.

"action": "game_start"

Infos

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
}

Map

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}
    ]
}

State

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}
]

Anatomy of a response

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}
]

Need more information?

If you think this documentation is not clear (read: suck), open an issue / submit a pull request!