Hex

Game logic

Game Logic code
def init_game(game_settings):
    state = {
        'player_turn_idx':  0,
        'pieces':  []
    }
    return state

def place_piece(state, row, column):
    WIDTH = 11
    HEIGHT = 11
    INVALID_ACTION_NOT_YOUR_TURN = "Not your turn."
    INVALID_ACTION_OUT_OF_BOUND = "Location is out of bound."
    INVALID_ACTION_ALREADY_PLACED = "A piece is already placed here."
    if __is_out_of_bound(row, column, WIDTH, HEIGHT):
        raise InvalidActionError(INVALID_ACTION_OUT_OF_BOUND)
    for piece in state['pieces']:
        if piece['row'] == row and piece['column'] == column:
            raise InvalidActionError(INVALID_ACTION_ALREADY_PLACED)
    player_idx = state['player_turn_idx']
    state['pieces'].append({
        'row': row, 
        'column': column, 
        'value': player_idx
        })

    state['player_turn_idx'] = (player_idx + 1) % 2
    return state

def __is_bottom(coord, board_width, board_height):
    return coord[0] == 0

def __is_top(coord, board_width, board_height):
    return coord[0] == board_height - 1

def __is_left(coord, board_width, board_height):
    return coord[1] == 0

def __is_right(coord, board_width, board_height):
    return coord[1] == board_width - 1

def __has_path_to(state, is_initial_func, is_target_func, player_idx, board_width, board_height):
    """
    Does a dfs search to see if a piece that satisfies the initial func
    is connected to a piece that satisfies sthe target func.
    <is_initial_func> is a function that returns a boolean if a piece
    satisfies the initial condition.
    """
    visited = set()
    stack = []
    has_target = False
    piece_coords = set()
    for piece in state['pieces']:
        coord = (piece['row'], piece['column'])
        if piece['value'] == player_idx:
            piece_coords.add(coord)
        else:
            continue
        if is_initial_func(coord, board_width, board_height):
            stack.append(coord)
        if is_target_func(coord, board_width, board_height):
            has_target = True
        
    if not is_initial_func or not has_target:
        return False

    while len(stack) > 0:
        this_coord = stack.pop()
        if is_target_func(this_coord, board_width, board_height):
            return True
        if this_coord in visited:
            continue
        visited.add(this_coord)
        neighbors = __get_neighbors(this_coord[0], this_coord[1], board_width, board_height)
        for n in neighbors:
            if n in piece_coords:
                stack.append(n)
    return False

def __is_out_of_bound(row, column, board_width, board_height):
    return (row < 0 or row >= board_height
        or column < 0 or column >= board_width)

def __get_neighbors(row, column, board_width, board_height):
    if __is_out_of_bound(row, column, board_width, board_height):
        return []
    neighbors = []
    if column > 0:
        neighbors.append((row, column - 1))
    if column < board_width - 1:
        neighbors.append((row, column + 1))
    if row > 0:
        neighbors.append((row - 1, column))
        if column < board_width - 1:
            neighbors.append((row - 1, column + 1))
    if row < board_height - 1:
        neighbors.append((row + 1, column))
        if column > 0:
            neighbors.append((row + 1, column - 1))
    return neighbors

def get_game_result(state):
    WIDTH = 11
    HEIGHT = 11
    player1_has_path = __has_path_to(
        state, __is_left, __is_right, 0, WIDTH, HEIGHT)
    player2_has_path = __has_path_to(
        state, __is_top, __is_bottom, 1, WIDTH, HEIGHT)
    if (player1_has_path and player2_has_path):
        return {
            "game_result": "Draw"
        }
    elif (player1_has_path and not player2_has_path):
        return {
            "game_result": "Winner",
            "winner_idx": 0
        }
    elif (player2_has_path and not player1_has_path):
        return {
            "game_result": "Winner",
            "winner_idx": 1
        }
    else:
        return {
            "game_result": "NoWinnerYet"
        }

Game state schema

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

  // All the pieces on the board that a player has put down. Player idx 0 tries
  // to connect left to right (column = 0 to column = 10). Plyaer idx 1 tries
  // to connect bottom to top (row = 0 to row = 10). 
  repeated Piece pieces = 2;
}
message Piece {
  // Row of the piece that has been placed. 
  int32 row = 1;

  // Column of the piece that has been placed.
  int32 column = 2;

  // value is the player_idx who placed this piece, can be 0 or 1.
  int32 value = 3;
}

Action schema

place_piece(int row, int column)

Start building now

Last updated