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)
Last updated