예제 #1
0
    def update_empty_intersection(self):
        n = self.board_size * self.board_size
        ownership = [UNDECIDED] * n
        visited = BitSet(n)
        adjacent_chains = BitSet(n)
        for x in range(self.board_size):
            for y in range(self.board_size):
                z = x * self.board_size + y
                if self._color[z] != EMPTY or visited.contains(z):
                    continue

                self._chain_size[z] = 0
                adjacent_to_black, adjacent_to_white = False, False
                adjacent_chains.clear()

                q = Queue(n)
                q.enqueue((x, y))
                visited.add(z)
                while not q.is_empty():
                    x, y = q.dequeue()
                    for dx, dy in _directions:
                        if self.on_board(x + dx, y + dy):
                            if self.color(x + dx, y + dy) == BLACK:
                                adjacent_chains.add(self.find(x + dx, y + dy))
                                adjacent_to_black = True
                            elif self.color(x + dx, y + dy) == WHITE:
                                adjacent_chains.add(self.find(x + dx, y + dy))
                                adjacent_to_white = True
                            elif not visited.contains(
                                (x + dx) * self.board_size + y + dy):
                                q.enqueue((x + dx, y + dy))
                                visited.add((x + dx) * self.board_size + y +
                                            dy)
                    self._parent[x * self.board_size + y] = z
                    self._chain_size[z] += 1

                if (adjacent_to_black and adjacent_to_white) \
                        or len(adjacent_chains) == 0:
                    ownership[z] = UNDECIDED
                elif len(adjacent_chains) == 1:
                    if adjacent_to_black:
                        ownership[z] = BLACK_EYE
                    else:
                        ownership[z] = WHITE_EYE
                else:
                    if adjacent_to_black:
                        ownership[z] = BLACK_OWNED
                    else:
                        ownership[z] = WHITE_OWNED

        return ownership
예제 #2
0
class TestQueue(unittest.TestCase):
    @classmethod
    def setUpClass(self):
        self.initial_list = [i for i in range(5)]

    @classmethod
    def tearDownClass(self):
        print("All tests for queue completed")

    def setUp(self):
        # print("Initializing queue")
        self.queue = Queue(8, 0, initial_iter=self.initial_list)

    def tearDown(self):
        pass

    def test_head(self):
        """
        Test getting top element        
        """
        self.assertEqual(4, self.queue.head())

    def test_tail(self):
        """
        Test getting top element        
        """
        self.assertEqual(0, self.queue.tail())

    def test_enqueue(self):
        """
        Test pushing to stack top
        """
        self.queue.enqueue(5)
        self.assertEqual(5, self.queue.tail())
        self.assertNotEqual(5, self.queue.head())
        self.assertEqual(4, self.queue.head())
        self.assertEqual(6, self.queue.size())

    def test_dequeue(self):
        """
        Test popping
        """
        for x in range(4, -1, -1):
            self.assertEqual(x, self.queue.dequeue())

        self.assertEqual(0, self.queue.size())
예제 #3
0
    def _remove(self, x, y):
        self._liberties[self.find(x, y)] = None

        n = self.board_size * self.board_size
        z = x * self.board_size + y
        color = self._color[z]

        # insert (x, y) into the queue, and mark it as "visited but not
        # processed" (color = _GRAY)
        # we abuse the _color array to store the BFS state here:
        #   - color = EMPTY: processed
        #   - color = BLACK: not visited (for black stones)
        #   - color = WHITE: not visited (for white stones)
        #   - color = _GRAY: visited (i.e., in queue) but not processed
        q = Queue(n)
        q.enqueue((x, y))
        self._color[z] = _GRAY
        adjacent_opponent_chains = SmallSet(4)
        while not q.is_empty():
            x, y = q.dequeue()
            adjacent_opponent_chains.clear()
            for dx, dy in _directions:
                if self.on_board(x + dx, y + dy):
                    # insert all the own adjacent stones that are not
                    # visited into the queue
                    if self.color(x + dx, y + dy) == color:
                        q.enqueue((x + dx, y + dy))
                        self._color[(x + dx) * self.board_size + y +
                                    dy] = _GRAY
                    # save opponent's stone chains that are adjacent to
                    # this new empty intersection
                    if self.color(x + dx, y + dy) == -color:
                        adjacent_opponent_chains.add(self.find(x + dx, y + dy))
            # the new empty intersection (x, y) provides liberty to its
            # adjacent stone chains
            z = x * self.board_size + y
            for c in adjacent_opponent_chains:
                if self._liberties[c] is None:
                    self._liberties[c] = BitSet(n)
                self._liberties[c].add(z)
            self._color[z] = EMPTY
예제 #4
0
파일: resign.py 프로젝트: xiao-liu/zeta-go
class ResignManager:
    def __init__(self, conf):
        self._regret_frac = conf.RESIGN_REGRET_FRAC
        self._histories = Queue(conf.NUM_RESIGN_SAMPLES)
        self._threshold = -1.0

    def threshold(self):
        return -1.0 if not self._histories.is_full() else self._threshold

    def add(self, history, result):
        if len(history) == 0:
            return

        # suppose the resignation values of a game are
        # [v_0, v_1, v_2, ...], we say time t is resignation eligible
        # if it is possible to choose an appropriate threshold such
        # that the game resigns at t
        # for example, t = 0 is always resignation eligible
        # t = 1 is resignation eligible if and only if v_1 < v_0,
        # otherwise it is impossible to find a threshold such that the
        # game resigns at t = 1
        # it is not difficult to see that if t is resignation eligible,
        # the next resignation eligible time is the smallest t' such
        # that t' > t and v_t' < v_t

        # keep only the resignation eligible time and discard the rest
        history_ = [history[0]]
        for t in range(1, len(history)):
            if history[t][0] < history_[-1][0]:
                history_.append(history[t])

        # update the regret
        # regret = 1 means a false positive resignation, i.e., the game
        # could have been won if the player had not resigned
        # regret = 0 otherwise
        for i in range(len(history_)):
            # history_[i][1] originally stores the player
            # after regret is calculated, the player information is no
            # longer needed, we reuse the space to store regret
            history_[i][1] = 1 if history_[i][1] == result else 0

        # discard the earliest history and save the current history
        if self._histories.is_full():
            self._histories.dequeue()
        self._histories.enqueue(history_)

        # update the threshold when we get sufficient samples
        if self._histories.is_full():
            self._update_threshold()

    def _update_threshold(self):
        # sort all the resignation values in descending order
        resign_values = []
        for history in self._histories:
            resign_values += [x[0] for x in history]
        resign_values = sorted(resign_values, reverse=True)

        # search for the threshold
        pointers = [0] * len(self._histories)
        i = 0
        while i < len(resign_values):
            threshold = resign_values[i]

            # calculate the number of regretful resignations for this
            # candidate threshold
            regrets = 0
            for j in range(len(self._histories)):
                history = self._histories[j]
                while pointers[j] < len(history) and \
                        history[pointers[j]][0] > threshold:
                    pointers[j] += 1
                if pointers[j] < len(history):
                    regrets += history[pointers[j]][1]

            if regrets <= self._regret_frac * len(self._histories):
                self._threshold = threshold
                return

            # find the next candidate threshold
            j = i + 1
            while j < len(resign_values) and \
                    resign_values[j] == resign_values[i]:
                j += 1
            i = j

        self._threshold = -1.0
예제 #5
0
    def _level_order_traversal(self, starting_node=None, include_none=True):
        """
        Traverses the binary tree from top level to bottom and saves data/height/depth for testing purpose

        Args:
            starting_node (TreeNode): traverse the subtree rooted at given starting node
            include_none (bool): True if including None node in binary tree

        Returns:
            data_array ([3-tuple]): [(data saved in node, height, depth)]
        """
        data_array = []

        # returns empty list if the binary tree is empty
        if self._root is None:
            return data_array

        # traverse from root if starting node is not given
        if starting_node is None:
            starting_node = self._root

        queue = Queue()
        # (data, height, depth, left child, right child)
        queue.enqueue((starting_node.data(), starting_node.height(),
                       starting_node.depth(), starting_node.left_child(),
                       starting_node.right_child()))

        while not queue.empty():
            top_node = queue.dequeue()

            if top_node[0] is not None:
                # if it's a non-trival node, append data/height/depth
                data_array.append((top_node[0], top_node[1], top_node[2]))
            else:
                data_array.append(None)

            # if the node is not on the deepest level, left and right child can be added into queue and visited soon
            if top_node[2] < self.depth():
                top_node_left_child, top_node_right_child = top_node[
                    3], top_node[4]

                entry = [None, top_node[1] - 1, top_node[2] + 1, None, None]
                if top_node_left_child is not None:
                    entry[0] = top_node_left_child.data()
                    entry[3] = top_node_left_child.left_child()
                    entry[4] = top_node_left_child.right_child()
                queue.enqueue(tuple(entry))

                entry = [None, top_node[1] - 1, top_node[2] + 1, None, None]
                if top_node_right_child is not None:
                    entry[0] = top_node_right_child.data()
                    entry[3] = top_node_right_child.left_child()
                    entry[4] = top_node_right_child.right_child()
                queue.enqueue(tuple(entry))

        # if include_none == False, filter out the None element
        if not include_none:
            data_array = list(filter(lambda x: x is not None, data_array))

        # remove all None at the end of list since they are trival and not informative
        while data_array[-1] is None:
            data_array.pop()

        return data_array