Exemple #1
0
 def get_game_ended(state):
     """
     :param state:
     :return: 0/1 = game not ended / game ended respectively
     """
     m, n = state_utils.get_board_size(state)
     return int(np.count_nonzero(state[govars.DONE_CHNL] == 1) == m * n)
Exemple #2
0
    def get_areas(state):
        '''
        Return black area, white area
        Use DFS helper to find territory.
        '''

        m, n = state_utils.get_board_size(state)
        visited = np.zeros((m, n), dtype=np.bool)
        black_area = 0
        white_area = 0

        # loop through each intersection on board
        for r, c in itertools.product(range(m), range(n)):
            # count pieces towards area
            if state[BLACK][r, c] > 0:
                black_area += 1
            elif state[WHITE][r, c] > 0:
                white_area += 1

            # do DFS on unvisited territory
            elif not visited[r, c]:
                player, area = state_utils.explore_territory(
                    state, (r, c), visited)

                # add area to corresponding player
                if player == BLACK:  # BLACK
                    black_area += area
                elif player == WHITE:  # WHITE
                    white_area += area

        return black_area, white_area
Exemple #3
0
 def get_action_size(state=None, board_size: int = None):
     # return number of actions
     if state is not None:
         m, n = state_utils.get_board_size(state)
     elif board_size is not None:
         m, n = board_size, board_size
     else:
         raise RuntimeError('No argument passed')
     return m * n + 1
Exemple #4
0
    def get_next_states(state, batch_action1d, group_map=None, canonical=False):
        """
        Does not change the given state
        """
        if group_map is None:
            group_map = state_utils.get_group_map(state)

        m, n = state_utils.get_board_size(state)
        pass_idcs = np.where(batch_action1d == m * n)
        non_pass_idcs = np.where(batch_action1d != m * n)

        batch_size = len(batch_action1d)
        board_shape = state.shape[1:]
        batch_action2d = np.empty((batch_size, 2), dtype=np.int)
        batch_action2d[:, 0] = batch_action1d // n
        batch_action2d[:, 1] = batch_action1d % n
        batch_action2d[np.where(batch_action1d == m * n)] = 0

        player = state_utils.get_turn(state)
        opponent = 1 - player
        previously_passed = GoGame.get_prev_player_passed(state)

        batch_states = np.tile(state, (batch_size, 1, 1, 1))

        # Check move is valid
        assert (batch_states[non_pass_idcs, govars.INVD_CHNL, batch_action2d[non_pass_idcs, 0], batch_action2d[
            non_pass_idcs, 1]] == 0).all(), "Invalid move"

        batch_group_maps = [[group_map[0].copy(), group_map[1].copy()] for _ in range(batch_size)]
        batch_single_kill = [None for _ in range(batch_size)]
        batch_killed_groups = [set() for _ in range(batch_size)]

        batch_adj_locs, batch_surrounded = state_utils.get_batch_adj_data(state, batch_action2d)

        if previously_passed:
            batch_states[pass_idcs, govars.DONE_CHNL] = 1
        else:
            batch_states[pass_idcs, govars.PASS_CHNL] = 1

        # Non passes
        batch_states[non_pass_idcs, govars.PASS_CHNL] = 0

        # Add pieces
        batch_states[non_pass_idcs, player, batch_action2d[non_pass_idcs, 0], batch_action2d[non_pass_idcs, 1]] = 1

        batch_data = enumerate(zip(batch_action1d, batch_action2d, batch_states, batch_group_maps, batch_adj_locs,
                                   batch_killed_groups))

        for i, (action_1d, action_2d, state, group_map, adj_locs, killed_groups) in batch_data:
            # if the current player passes
            if action_1d == m * n:
                continue

            action_2d = tuple(action_2d)

            # Get all adjacent information
            adj_own_groups, adj_opp_groups = state_utils.get_adjacent_groups(group_map, adj_locs, player)

            # Go through opponent groups
            for group in adj_opp_groups:
                assert action_2d in group.liberties, (action_2d, player, group, state)
                if len(group.liberties) <= 1:
                    # Killed group
                    killed_groups.add(group)

                    # Remove group in board and group map
                    for loc in group.locations:
                        state[opponent, loc[0], loc[1]] = 0
                    group_map[opponent].remove(group)

                    # Metric for ko-protection
                    if len(group.locations) <= 1 and batch_single_kill[i] is None:
                        batch_single_kill[i] = next(iter(group.locations))

            adj_opp_groups.difference_update(killed_groups)

            # Update surviving adjacent opponent groups by removing liberties by the new action
            for opp_group in adj_opp_groups:
                assert action_2d in opp_group.liberties, (action_2d, opp_group, adj_opp_groups)

                # New group copy
                group_map[opponent].remove(opp_group)
                opp_group = opp_group.copy()
                group_map[opponent].add(opp_group)

                opp_group.liberties.remove(action_2d)

            # Update adjacent own groups that are merged with the action
            if len(adj_own_groups) > 0:
                merged_group = adj_own_groups.pop()
                group_map[player].remove(merged_group)
                merged_group = merged_group.copy()
            else:
                merged_group = govars.Group()

            group_map[player].add(merged_group)

            # Locations from action and adjacent groups
            merged_group.locations.add(action_2d)

            for own_group in adj_own_groups:
                merged_group.locations.update(own_group.locations)
                merged_group.liberties.update(own_group.liberties)
                group_map[player].remove(own_group)

            # Liberties from action
            for adj_loc in adj_locs:
                if np.count_nonzero(state[[govars.BLACK, govars.WHITE], adj_loc[0], adj_loc[1]]) == 0:
                    merged_group.liberties.add(adj_loc)

            if action_2d in merged_group.liberties:
                merged_group.liberties.remove(action_2d)

            # More work to do if we killed
            if len(killed_groups) > 0:
                killed_map = np.zeros(board_shape)
                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_idcs = set(zip(*np.nonzero(state[player] * killed_liberties)))
                groups_to_update = set()
                for group in group_map[player]:
                    if not affected_idcs.isdisjoint(group.locations):
                        groups_to_update.add(group)

                all_pieces = np.sum(state[[govars.BLACK, govars.WHITE]], axis=0)
                empties = (1 - all_pieces)
                for group in groups_to_update:
                    group_matrix = np.zeros(board_shape)
                    for loc in group.locations:
                        group_matrix[loc] = 1

                    additional_liberties = ndimage.binary_dilation(group_matrix) * empties * killed_map
                    additional_liberties = set(zip(*np.where(additional_liberties)))

                    group_map[player].remove(group)
                    group = group.copy()
                    group_map[player].add(group)

                    group.liberties.update(additional_liberties)

        # Update illegal moves
        batch_states[:, govars.INVD_CHNL] = state_utils.get_batch_invalid_moves(batch_states, batch_group_maps, player)

        # If group was one piece, and location is surrounded by opponents,
        # activate ko protection
        for i, (single_kill, killed_groups, surrounded) in enumerate(zip(batch_single_kill, batch_killed_groups,
                                                                         batch_surrounded)):
            if single_kill is not None and len(killed_groups) == 1 and surrounded:
                state[govars.INVD_CHNL, single_kill[0], single_kill[1]] = 1

        # Switch turn
        state_utils.batch_set_turn(batch_states)

        if canonical:
            GoGame.set_batch_canonical_form(batch_states, batch_group_maps, opponent)

        return batch_states, batch_group_maps
Exemple #5
0
 def get_prev_player_passed(state):
     m, n = state_utils.get_board_size(state)
     return np.count_nonzero(state[govars.PASS_CHNL] == 1) == m * n
Exemple #6
0
    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
Exemple #7
0
    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