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
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())
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
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
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