Quoridor
Game logic
Game Logic code
BOARD_WIDTH = 9
BOARD_HEIGHT = 9
def init_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 state
def move_up(state):
return __move_helper(state, __can_move_up, "up")
def move_down(state):
return __move_helper(state, __can_move_down, "down")
def move_left(state):
return __move_helper(state, __can_move_left, "left")
def move_right(state):
return __move_helper(state, __can_move_right, "right")
def place_horizontal_wall(state, row, column):
new_state = __place_wall_helper(state, row, column, True)
return new_state
def place_vertical_wall(state, row, column):
new_state = __place_wall_helper(state, row, column, False)
return new_state
def get_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:
raise InvalidActionError("no more walls to place")
if row < 0 or row >= BOARD_HEIGHT - 1 or \
column < 0 or column >= BOARD_WIDTH - 1:
raise InvalidActionError("can't place wall there")
for w in state['walls']:
if w['row'] == row and w['column'] == column and w['is_horizontal'] != is_horizontal:
raise InvalidActionError("overlapping with another wall")
if w['is_horizontal'] and is_horizontal and w['row'] == row:
if abs(w['column'] - column) <= 1:
raise InvalidActionError("overlapping with another wall")
if not w['is_horizontal'] and not is_horizontal and w['column'] == column:
if abs(w['row'] - row) <= 1:
raise InvalidActionError("overlapping with another wall")
state['walls'].append({
'row': row,
'column': column,
'is_horizontal': is_horizontal
})
if not __has_path_to(state, BOARD_HEIGHT - 1, state['player_positions'][0]):
state['walls'].pop() # Just clean up so we don't modify the state accidentally
raise InvalidActionError("wall seals the path to the top")
if not __has_path_to(state, 0, state['player_positions'][1]):
state['walls'].pop() # Just clean up so we don't modify the state accidentally
raise InvalidActionError("wall seals the path to the bottom")
state["player_turn_idx"] = (state["player_turn_idx"] + 1) % 2
state['remaining_wall_count'][player_idx] -= 1
return state
def __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'])
if not move_coordinate:
raise InvalidActionError("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])
if not move_more:
raise InvalidActionError("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_idx
return state
def __can_move_up(wall_set, r, c):
if (r, c, True) not in wall_set and \
(r, c - 1, True) not in wall_set and \
r + 1 < BOARD_HEIGHT:
return (r + 1, c)
return None
def __can_move_down(wall_set, r, c):
if (r - 1, c, True) not in wall_set and \
(r - 1, c - 1, True) not in wall_set and \
r - 1 >= 0:
return (r - 1, c)
return None
def __can_move_left(wall_set, r, c):
if (r, c - 1, False) not in wall_set and \
(r - 1, c - 1, False) not in wall_set and \
c - 1 >= 0:
return (r, c - 1)
return None
def __can_move_right(wall_set, r, c):
if (r, c, False) not in wall_set and \
(r - 1, c, False) not in wall_set and \
c + 1 < BOARD_WIDTH:
return (r, c + 1)
return None
def __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']])
while len(stack) > 0:
node = stack.pop()
if node[0] == target_row:
return True
if 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)
return False
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;
// Array of player positions, of length 2
repeated Position player_positions = 2;
// These are the walls that have been placed on the board.
repeated Wall walls = 3;
// Array of length 2, storing the number of wall pieces remaining for each player
// that can still be placed onto the board.
repeated int32 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;
}
message Position {
int32 row = 1;
int32 column = 2;
}
Action schema
move_up()
move_down()
move_left()
move_right()
place_horizontal_wall(int row, int column)
place_vertical_wall(int row, int column)
Last updated