definit_game(game_settings): state ={'player_turn_idx':0,'pieces': []}return statedefplace_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):raiseInvalidActionError(INVALID_ACTION_OUT_OF_BOUND)for piece in state['pieces']:if piece['row']== row and piece['column']== column:raiseInvalidActionError(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) %2return statedef__is_bottom(coord,board_width,board_height):return coord[0]==0def__is_top(coord,board_width,board_height):return coord[0]== board_height -1def__is_left(coord,board_width,board_height):return coord[1]==0def__is_right(coord,board_width,board_height):return coord[1]== board_width -1def__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:continueifis_initial_func(coord, board_width, board_height): stack.append(coord)ifis_target_func(coord, board_width, board_height): has_target =Trueifnot is_initial_func ornot has_target:returnFalsewhilelen(stack)>0: this_coord = stack.pop()ifis_target_func(this_coord, board_width, board_height):returnTrueif 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)returnFalsedef__is_out_of_bound(row,column,board_width,board_height):return (row <0or row >= board_heightor column <0or 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 neighborsdefget_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 andnot player2_has_path):return{"game_result":"Winner","winner_idx":0}elif (player2_has_path andnot 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 1int32 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). repeatedPiece 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;}