Get action: game state

On the bot's turn, it receives a game state, which contains all the information the bot needs to understand the state of the game in order to determine the action to take. We use protocol buffer to document the game state schema.

Whose turn is it?

Each game has a list of participating bots [player0, player1, ..., playerN]. Which bot should make the next move is based on player_turn_idx. When player_turn_idx = 0, then the first player bot will calculate and submit its move, which is all handled automatically by the server. All game states on Botpot will contain a player_turn_idx that the bot can use to deduce which player it is playing.

Examples

The bot needs to know the values of the 9 squares, and whether the bot is playing circles or crosses. If state.player_turn_idx is 0, then the bot is playing circles. Otherwise, it is playing crosses. The protocol buffer schema for the Tic-Tac-Toe game state looks like

message State {
  // Required field to indicate the player who should be making the next move
  // Values = 0 or 1
  int32 player_turn_idx = 1;

  /*
  Array of size 9 initialized to -1, representing an empty 3 by 3 board.
  The values of board can be -1, 0, or 1. 
  Placing a piece in index x will set the value of board[x] to the player's index.
  Array is row major order: index 0 is top left, index 2 is top right, index 6 is 
  bottom left, and index 8 is bottom right.
  */
  repeated int32 board = 2;
}

The Tic-Tac-Toe bot below illustrates how one can use these fields to compute the next action.

def get_action(state, api):
    my_piece = state['player_turn_idx']
    board = state['board']
    # check if the top row is filled with my piece
    if board[0] == my_piece and board[1] == my_piece:
        # ...place_piece on the top right

In Reversi, the bot needs to know the values of the 64 squares. We can use a similar schema as Tic-Tac-Toe. To make it easier to code the bot, we also pre-computed a list of all the valid moves that a bot can make. The bot can select from one of the valid moves to return as its action.

message State {
  // Required field to indicate the player who should be making the next move
  // Values = 0 or 1    
  int32 player_turn_idx = 1;

  // An array of size 64 stored in row major order, so index 0 is top left, 
  // index 7 is top right, index 56 is bottom left, and index 63 is bottom right.
  // Values can be -1, 0, or 1, where -1 represents an empty spot, 0 is a piece
  // made by player_idx 0 and 1 is a piece made by player_idx 1.
  repeated int32 game_board = 2;

  // Precomputed array of all the valid moves. Players should select an index from
  // this list to make a move. 
  repeated ValidMove valid_moves = 3;
}
// The 8 directions to flip opponent disks.
enum direction {
  ABOVE = 0;
  BELOW = 1;
  LEFT = 2;
  RIGHT = 3;
  ABOVE_LEFT = 4;
  ABOVE_RIGHT = 5;
  BELOW_LEFT = 6;
  BELOW_RIGHT = 7;
}

/*
Each ValidMove is an index that the current player can place his piece on.
It also contains information about the all directions that will flip the 
opponent pieces. 
*/
message ValidMove {
  int32 index = 1;
  repeated direction valid_directions = 2;
}

Different game perspectives

Some games such as Chess, Tic-Tac-Toe, and Checkers are perfect information, where there is no hidden information so all the bots know everything about the game. On the other hand, Texas Hold'em is an example of an imperfect information game. As shown in Fig. 1, our server stores a game state from the global perspective with no hidden information. This global game state is used to derive what each bot is supposed to see. Bot 1 is only sent its cards, which is a pair of K's. Similarly, Bot 2 is sent its pair of 3's. Both bots can also see the shared cards in the middle.

When a bot makes an action, the server can compute the new global game state and the bots' perspective game states.

Examples

message State {
  enum Phase {
    PLACE_SHIP = 0;
    FIRE_SHOT = 1;
  }

  // Required field to indicate the player who should be making the next move
  // Values = 0 or 1
  int32 player_turn_idx = 1;

  // This will be length 2 for the two players. The player can only see their own ships
  // but will have information on the other fields such as the shots and sunken ships.
  repeated PlayerInfo player_info = 2;

  // The game starts off with PLACE_SHIP phase. Once both players placed their ships,
  // the phase changes to FIRE_SHOT until one of the player's ships are all sunk.
  Phase phase = 3;
}

Last updated