import randomdefinit_game(game_settings): size = game_settings.get('Size')if size =='Any': size = random.choice(['2x2', '3x3', '7x7'])if size =='2x2': width =2 height =2elif size =='3x3': width =3 height =3elif size =='7x7': width =7 height =7 new_board = []for r inrange(height):for c inrange(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 boxdefdraw_line(state,row,column,loc):if row <0or row >= state['board_height']:raiseInvalidActionError("invalid (row, column)")if column <0or column >= state['board_width']:raiseInvalidActionError("invalid (row, column)")if loc !="top"and loc !="bottom"and loc !="left"and loc !="right":raiseInvalidActionError("invalid location. choose top, bottom, left, or right") box_index = row * state['board_width']+ columnif state['board'][box_index]['closed_box_player_idx'] !=-1:raiseInvalidActionError("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 =-1if loc =="top"and row -1>-1: opposite_edge ="bottom" other_box_index = (row -1) * state['board_width']+ columnelif loc =="bottom"and row +1< state['board_height']: opposite_edge ="top" other_box_index = (row +1) * state['board_width']+ columnelif 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_idxelse: state['player_turn_idx']= (player_idx +1) %2return statedef__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:raiseInvalidActionError(INVALID_ACTION_LINE_DRAWN)else: state['board'][box_index]['top_line'] = player_idxelif loc =="bottom":if state['board'][box_index]['bottom_line'] !=-1:raiseInvalidActionError(INVALID_ACTION_LINE_DRAWN)else: state['board'][box_index]['bottom_line'] = player_idxelif loc =="left":if state['board'][box_index]['left_line'] !=-1:raiseInvalidActionError(INVALID_ACTION_LINE_DRAWN)else: state['board'][box_index]['left_line'] = player_idxelif loc =="right":if state['board'][box_index]['right_line'] !=-1:raiseInvalidActionError(INVALID_ACTION_LINE_DRAWN)else: state['board'][box_index]['right_line'] = player_idxreturn__check_closed(state, player_idx, box_index)# Return true if this box is closeddef__check_closed(state,player_idx,box_index):if state['board'][box_index]['top_line'] !=-1and state['board'][box_index]['bottom_line'] !=-1and state['board'][box_index]['right_line'] !=-1and state['board'][box_index]['left_line'] !=-1: state['board'][box_index]['closed_box_player_idx'] = player_idxreturnTruereturnFalsedefget_game_result(state): closed_boxes = [box['closed_box_player_idx']for box in state['board']]if (-1in 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 1int32 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. repeatedBox board =2;// Number of horizontal boxes in the gridint32 board_width =3;// Number of vertical boxes in the gridint32 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)