Dots and Boxes

Game logic

Game Logic code
import random
def init_game(game_settings):
    size = game_settings.get('Size')
    if size == 'Any':
        size = random.choice(['2x2', '3x3', '7x7'])
    if size == '2x2':
        width = 2
        height = 2
    elif size == '3x3':
        width = 3
        height = 3
    elif size == '7x7':
        width = 7
        height = 7
    new_board = []
    for r in range(height):
        for c in range(width):
            new_board.append({
                'row': r,
                'column': c,
                'closed_box_player_idx': -1,
                'top_line': -1,
                'bottom_line': -1,
                'right_line': -1,
                'left_line': -1
                })
    state = {
        'player_turn_idx': 0,
        'board': new_board,
        'board_width': width,
        'board_height': height
    }
    return state

#User draws a line for the box in (row, column) at the loc location (i.e. top, bottom, left, right) of that box
def draw_line(state, row, column, loc):
    if row < 0 or row >= state['board_height']:
        raise InvalidActionError("invalid (row, column)")
    if column < 0 or column >= state['board_width']:
        raise InvalidActionError("invalid (row, column)")
    if loc != "top" and loc != "bottom" and loc != "left" and loc != "right":
        raise InvalidActionError("invalid location. choose top, bottom, left, or right")
    box_index = row * state['board_width'] + column
    if state['board'][box_index]['closed_box_player_idx'] != -1:
        raise InvalidActionError("this box is already closed")

    player_idx = state['player_turn_idx']
    is_this_box_closed = __update_box(state, player_idx, box_index, loc)
    is_other_box_closed = False

    #Find any neighboring boxes, and update them
    other_box_index = -1
    if loc == "top" and row - 1 > -1:
        opposite_edge = "bottom"
        other_box_index = (row - 1) * state['board_width'] + column
    elif loc == "bottom" and row + 1 < state['board_height']:
        opposite_edge = "top"
        other_box_index = (row + 1) * state['board_width'] + column
    elif loc == "left" and column - 1 > -1:
        opposite_edge = "right"
        other_box_index = row * state['board_width'] + (column - 1)
    elif loc == "right" and column + 1 < state['board_width']:
        opposite_edge = "left"
        other_box_index = row * state['board_width'] + (column + 1)
    if other_box_index != -1:
        is_other_box_closed = __update_box(state, player_idx, other_box_index, opposite_edge)

    if is_this_box_closed or is_other_box_closed:
        state['player_turn_idx'] = player_idx
    else:
        state['player_turn_idx'] = (player_idx + 1) % 2
    
    return state

def __update_box(state, player_idx, box_index, loc):
    INVALID_ACTION_LINE_DRAWN = "someone already drew a line here"
    if loc == "top":
        if state['board'][box_index]['top_line'] != -1:
            raise InvalidActionError(INVALID_ACTION_LINE_DRAWN)
        else:
            state['board'][box_index]['top_line'] = player_idx
    elif loc == "bottom":
        if state['board'][box_index]['bottom_line'] != -1:
            raise InvalidActionError(INVALID_ACTION_LINE_DRAWN)
        else:
            state['board'][box_index]['bottom_line'] = player_idx
    elif loc == "left":
        if state['board'][box_index]['left_line'] != -1:
            raise InvalidActionError(INVALID_ACTION_LINE_DRAWN)
        else:
            state['board'][box_index]['left_line'] = player_idx
    elif loc == "right":
        if state['board'][box_index]['right_line'] != -1:
            raise InvalidActionError(INVALID_ACTION_LINE_DRAWN)
        else:
            state['board'][box_index]['right_line'] = player_idx
    return __check_closed(state, player_idx, box_index)

# Return true if this box is closed
def __check_closed(state, player_idx, box_index):
    if state['board'][box_index]['top_line'] != -1 and state['board'][box_index]['bottom_line'] != -1 and state['board'][box_index]['right_line'] != -1 and state['board'][box_index]['left_line'] != -1:
        state['board'][box_index]['closed_box_player_idx'] = player_idx
        return True
    return False

def get_game_result(state):
    closed_boxes = [box['closed_box_player_idx'] for box in state['board']]
    if (-1 in closed_boxes):
        return {
            "game_result": "NoWinnerYet"
        }
    player1_score = closed_boxes.count(0)
    player2_score = closed_boxes.count(1)
    if (player1_score > player2_score):
        return {
            "game_result": "Winner",
            "winner_idx": 0
        }
    elif (player1_score < player2_score):
        return {
            "game_result": "Winner",
            "winner_idx": 1
        }
    return {
        "game_result": "Draw"
    }

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;

  // The array is stored in row major order, so index 0 is the top left and 
  // last index is the bottom right. In a size 2x2 board, index 1 is the 
  // top right board. 
  repeated Box board = 2;

  // Number of horizontal boxes in the grid
  int32 board_width = 3;

  // Number of vertical boxes in the grid
  int32 board_height = 4;
}
/*
Represents a 1x1 box. Closing this box requires 4 lines. 
*/
message Box {
  // The row of this box in the grid of boxes
  int32 row = 1;

  // The column of this box in the grid of boxes
  int32 column = 2;

  // The player who closed this box. Values can be -1, 0, or 1.
  // -1 means the box isn't closed yet.
  int32 closed_box_player_idx = 3;

  // Values can be -1, 0, 1, representing who drew this line on this box.
  int32 top_line = 4;

  // Values can be -1, 0, 1, representing who drew this line on this box.
  int32 bottom_line = 5;

  // Values can be -1, 0, 1, representing who drew this line on this box.
  int32 right_line = 6;

  // Values can be -1, 0, 1, representing who drew this line on this box.
  int32 left_line = 7;
}

Action schema

enum Location {
  top = 0;
  bottom = 1;
  left = 2;
  right = 3;
}

draw_line(int row, int column, Location loc)

Start building now

Last updated