def canonical_form(state): state = np.copy(state) if turn(state) == govars.WHITE: channels = np.arange(govars.NUM_CHNLS) channels[govars.BLACK] = govars.WHITE channels[govars.WHITE] = govars.BLACK state = state[channels] state_utils.set_turn(state) return state
def next_state(state, action1d, canonical=False): # Deep copy the state to modify state = np.copy(state) # Initialize basic variables board_shape = state.shape[1:] pass_idx = np.prod(board_shape) passed = action1d == pass_idx action2d = action1d // board_shape[0], action1d % board_shape[1] player = turn(state) previously_passed = prev_player_passed(state) ko_protect = None if passed: # We passed state[govars.PASS_CHNL] = 1 if previously_passed: # Game ended state[govars.DONE_CHNL] = 1 else: # Move was not pass state[govars.PASS_CHNL] = 0 # Assert move is valid assert state[govars.INVD_CHNL, action2d[0], action2d[1]] == 0, ("Invalid move", action2d) # Add piece state[player, action2d[0], action2d[1]] = 1 # Get adjacent location and check whether the piece will be surrounded by opponent's piece adj_locs, surrounded = state_utils.adj_data(state, action2d, player) # Update pieces killed_groups = state_utils.update_pieces(state, adj_locs, player) # If only killed one group, and that one group was one piece, and piece set is surrounded, # activate ko protection if len(killed_groups) == 1 and surrounded: killed_group = killed_groups[0] if len(killed_group) == 1: ko_protect = killed_group[0] # Update invalid moves state[govars.INVD_CHNL] = state_utils.compute_invalid_moves( state, player, ko_protect) # Switch turn state_utils.set_turn(state) if canonical: # Set canonical form state = canonical_form(state) return state
def get_canonical_form(state): """ The returned state is a shallow copy of the given state :param state: :param player: :return: """ player = GoGame.get_turn(state) if player == govars.BLACK: return state else: assert player == govars.WHITE num_channels = state.shape[0] channels = np.arange(num_channels) channels[govars.BLACK] = govars.WHITE channels[govars.WHITE] = govars.BLACK can_state = state[channels] state_utils.set_turn(can_state) return can_state
def get_canonical_form(state): """ The returned state is a seperate copy of the given state :param state: :param player: :return: """ state = np.copy(state) player = GoGame.get_turn(state) if player == BLACK: return state else: assert player == WHITE num_channels = state.shape[0] channels = np.arange(num_channels) channels[BLACK] = WHITE channels[WHITE] = BLACK state = state[channels] state_utils.set_turn(state) return state
def get_next_state(state, action, group_map=None, inplace=False): """ Does not change the given state :param state: :param action: :return: The next state """ # check if game is already over if GoGame.get_game_ended(state) != 0: raise Exception('Attempt to step at {} after game is over'.format(action)) state = np.copy(state) single_kill = None empty_adjacents_before_kill = None if group_map is None: group_map = state_utils.get_all_groups(state) # if the current player passes if action == GoGame.get_action_size(state) - 1: # if two consecutive passes, game is over if GoGame.get_prev_player_passed(state): state_utils.set_game_ended(state) else: state_utils.set_prev_player_passed(state) else: # This move was not a pass state_utils.set_prev_player_passed(state, 0) player = state_utils.get_turn(state) opponent = 1 - player m, n = state_utils.get_board_size(state) # convert the move to 2d action = (action // m, action % n) # Check move is valid if not state_utils.is_within_bounds(state, action): raise Exception("{} Not Within bounds".format(action)) elif state[INVD_CHNL, action[0], action[1]] > 0: raise Exception("Invalid Move", action, state) # Get all adjacent information adjacent_locations = state_utils.get_adjacent_locations(state, action) adj_own_groups, adj_opp_groups = state_utils.get_adjacent_groups(state, group_map, adjacent_locations, player) if not inplace: # Start new group map group_map = np.copy(group_map) # Go through opponent groups killed_groups = set() empty_adjacents_before_kill = adjacent_locations.copy() for group in adj_opp_groups: assert action in group.liberties, (action, group, state[[BLACK, WHITE]]) empty_adjacents_before_kill.difference_update(group.locations) if len(group.liberties) <= 1: # Killed group killed_groups.add(group) # Remove group in board and group map for loc in group.locations: group_map[loc] = None state[opponent, loc[0], loc[1]] = 0 # Metric for ko-protection if len(group.locations) <= 1: if single_kill is not None: single_kill = None else: single_kill = next(iter(group.locations)) adj_opp_groups.difference_update(killed_groups) # Add the piece! state[player, action[0], action[1]] = 1 # Update surviving adjacent opponent groups by removing liberties by the new action for opp_group in adj_opp_groups: assert action in opp_group.liberties, (action, opp_group, adj_opp_groups) if not inplace: # New group copy opp_group = opp_group.copy() for loc in opp_group.locations: group_map[loc] = opp_group opp_group.liberties.remove(action) # Update adjacent own groups that are merged with the action if len(adj_own_groups) > 0: merged_group = adj_own_groups.pop() if not inplace: merged_group = merged_group.copy() for loc in merged_group.locations: group_map[loc] = merged_group else: merged_group = Group() # Locations from action and adjacent groups merged_group.locations.add(action) group_map[action] = merged_group for own_group in adj_own_groups: for loc in own_group.locations: group_map[loc] = merged_group merged_group.locations.add(loc) merged_group.liberties.update(own_group.liberties) # Liberties from action for adj_loc in adjacent_locations: if np.count_nonzero(state[[BLACK, WHITE], adj_loc[0], adj_loc[1]]) == 0: merged_group.liberties.add(adj_loc) if action in merged_group.liberties: merged_group.liberties.remove(action) # More work to do if we killed if len(killed_groups) > 0: killed_map = np.zeros(state.shape[1:]) for group in killed_groups: for loc in group.locations: killed_map[loc] = 1 # Update own groups adjacent to opponent groups that we just killed killed_liberties = ndimage.binary_dilation(killed_map) affected_group_matrix = state[player] * killed_liberties groups_to_update = set(group_map[np.nonzero(affected_group_matrix)]) all_pieces = np.sum(state[[BLACK, WHITE]], axis=0) empties = (1 - all_pieces) for group in groups_to_update: group_matrix = group_map == group additional_liberties = ndimage.binary_dilation(group_matrix) * empties * killed_map additional_liberties = np.argwhere(additional_liberties) if not inplace: group = group.copy() for loc in group.locations: group_map[loc] = group for liberty in additional_liberties: group.liberties.add(tuple(liberty)) # Update illegal moves state_utils.set_invalid_moves(state, group_map) # If group was one piece, and location is surrounded by opponents, # activate ko protection if single_kill is not None and len(empty_adjacents_before_kill) <= 0: state[INVD_CHNL, single_kill[0], single_kill[1]] = 1 # Switch turn state_utils.set_turn(state) return state, group_map
def get_init_board(size, black_first=True): # return initial board (numpy board) state = np.zeros((6, size, size)) if not black_first: state_utils.set_turn(state) return state
def get_next_state(state, action): """ Does not change the given state :param state: :param action: :return: The next state """ # check if game is already over if GoGame.get_game_ended(state) != 0: raise Exception( 'Attempt to step at {} after game is over'.format(action)) state = np.copy(state) # if the current player passes if action == GoGame.get_action_size(state) - 1: # if two consecutive passes, game is over if GoGame.get_prev_player_passed(state): state_utils.set_game_ended(state) else: state_utils.set_prev_player_passed(state) # Update invalid channel state_utils.reset_invalid_moves(state) state_utils.add_invalid_moves(state) # Switch turn state_utils.set_turn(state) # Return event return state player = state_utils.get_turn(state) m, n = state_utils.get_board_size(state) # convert the move to 2d action = (action // m, action % n) # Check move is valid if not state_utils.is_within_bounds(state, action): raise Exception("{} Not Within bounds".format(action)) elif state[INVD_CHNL][action] > 0: raise Exception("Invalid Move", action, state) state_utils.reset_invalid_moves(state) # Get all adjacent groups _, opponent_groups = state_utils.get_adjacent_groups(state, action) # Go through opponent groups killed_single_piece = None empty_adjacents_before_kill = state_utils.get_adjacent_locations( state, action) for group in opponent_groups: empty_adjacents_before_kill = empty_adjacents_before_kill - group.locations if len(group.liberties) <= 1: assert action in group.liberties # Remove group in board for loc in group.locations: # TODO: Hardcoded other player. Make more generic state[1 - player][loc] = 0 # Metric for ko-protection if len(group.locations) <= 1: if killed_single_piece is not None: killed_single_piece = None else: killed_single_piece = group.locations.pop() # If group was one piece, and location is surrounded by opponents, # activate ko protection if killed_single_piece is not None and len( empty_adjacents_before_kill) <= 0: state[INVD_CHNL][killed_single_piece] = 1 # Add the piece! state[player][action] = 1 # Update illegal moves state_utils.add_invalid_moves(state) # This move was not a pass state_utils.set_prev_player_passed(state, 0) # Switch turn state_utils.set_turn(state) return state