BOARD_WIDTH =9BOARD_HEIGHT =9definit_game(game_settings): position1 ={'row':0,'column':4} position2 ={'row': BOARD_HEIGHT -1,'column':4} state ={'player_turn_idx':0,'player_positions': [position1, position2],'walls': [],'remaining_wall_count': [10,10]}return statedefmove_up(state):return__move_helper(state, __can_move_up, "up")defmove_down(state):return__move_helper(state, __can_move_down, "down")defmove_left(state):return__move_helper(state, __can_move_left, "left")defmove_right(state):return__move_helper(state, __can_move_right, "right")defplace_horizontal_wall(state,row,column): new_state =__place_wall_helper(state, row, column, True)return new_statedefplace_vertical_wall(state,row,column): new_state =__place_wall_helper(state, row, column, False)return new_statedefget_game_result(state):if state['player_positions'][0]['row'] == BOARD_HEIGHT -1:return{"game_result":"Winner","winner_idx":0}if state['player_positions'][1]['row'] ==0:return{"game_result":"Winner","winner_idx":1}return{"game_result":"NoWinnerYet"}def__place_wall_helper(state,row,column,is_horizontal): player_idx = state['player_turn_idx']if state['remaining_wall_count'][player_idx] <=0:raiseInvalidActionError("no more walls to place")if row <0or row >= BOARD_HEIGHT -1or\ column <0or column >= BOARD_WIDTH -1:raiseInvalidActionError("can't place wall there")for w in state['walls']:if w['row']== row and w['column']== column and w['is_horizontal']!= is_horizontal:raiseInvalidActionError("overlapping with another wall")if w['is_horizontal']and is_horizontal and w['row']== row:ifabs(w['column'] - column)<=1:raiseInvalidActionError("overlapping with another wall")ifnot w['is_horizontal']andnot is_horizontal and w['column']== column:ifabs(w['row'] - row)<=1:raiseInvalidActionError("overlapping with another wall") state['walls'].append({'row': row, 'column': column, 'is_horizontal': is_horizontal })ifnot__has_path_to(state, BOARD_HEIGHT -1, state['player_positions'][0]): state['walls'].pop()# Just clean up so we don't modify the state accidentallyraiseInvalidActionError("wall seals the path to the top")ifnot__has_path_to(state, 0, state['player_positions'][1]): state['walls'].pop()# Just clean up so we don't modify the state accidentallyraiseInvalidActionError("wall seals the path to the bottom") state["player_turn_idx"]= (state["player_turn_idx"]+1) %2 state['remaining_wall_count'][player_idx] -=1return statedef__move_helper(state,direction_func,direction_str): player_idx = state['player_turn_idx'] other_player_idx = (player_idx +1) %2 player_position = state['player_positions'][player_idx] other_player_position = state['player_positions'][other_player_idx] wall_set =set([(w['row'], w['column'], w['is_horizontal']) for w in state['walls']]) move_coordinate =direction_func(wall_set, player_position['row'], player_position['column'])ifnot move_coordinate:raiseInvalidActionError("cannot move "+ direction_str)if move_coordinate == (other_player_position['row'], other_player_position['column']): move_more =direction_func(wall_set, move_coordinate[0], move_coordinate[1])ifnot move_more:raiseInvalidActionError("cannot move "+ direction_str +" over other player") state['player_positions'][player_idx]['row'] = move_more[0] state['player_positions'][player_idx]['column'] = move_more[1]else: state['player_positions'][player_idx]['row'] = move_coordinate[0] state['player_positions'][player_idx]['column'] = move_coordinate[1] state['player_turn_idx']= other_player_idxreturn statedef__can_move_up(wall_set,r,c):if (r, c,True) notin wall_set and\ (r, c -1,True) notin wall_set and\ r +1< BOARD_HEIGHT:return (r +1, c)returnNonedef__can_move_down(wall_set,r,c):if (r -1, c,True) notin wall_set and\ (r -1, c -1,True) notin wall_set and\ r -1>=0:return (r -1, c)returnNonedef__can_move_left(wall_set,r,c):if (r, c -1,False) notin wall_set and\ (r -1, c -1,False) notin wall_set and\ c -1>=0:return (r, c -1)returnNonedef__can_move_right(wall_set,r,c):if (r, c,False) notin wall_set and\ (r -1, c,False) notin wall_set and\ c +1< BOARD_WIDTH:return (r, c +1)returnNonedef__has_path_to(state,target_row,position):""" Does a dfs search from position to target. return true if there is a path """ visited =set() stack = [(position['row'], position['column'])] wall_set =set([(w['row'], w['column'], w['is_horizontal']) for w in state['walls']])whilelen(stack)>0: node = stack.pop()if node[0]== target_row:returnTrueif node in visited:continue r, c = node visited.add(node) neighbors = [__can_move_up(wall_set, r, c),__can_move_down(wall_set, r, c),__can_move_right(wall_set, r, c),__can_move_left(wall_set, r, c)]for n in neighbors:if n: stack.append(n)returnFalse
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;// Array of player positions, of length 2repeatedPosition player_positions =2;// These are the walls that have been placed on the board.repeatedWall walls =3;// Array of length 2, storing the number of wall pieces remaining for each player// that can still be placed onto the board.repeatedint32 remaining_wall_count =4;}
/*
Walls have a length of 2.
A horizontal wall in <row 0, column 0> blocks player piece at both <row 0, column 0> and <row 0, column 1> from going up.
A vertical wall in <row 0, column 0> blocks player piece at both <row 0, column 0> and <row 1, column 0> from going right.
Both horizontal and vertical walls can be placed between rows 0-7 and columns 0-7 inclusive.
*/
message Wall {
int32 row = 1;
int32 column = 2;
bool is_horizontal = 3;
}