def legal_play(self, x, y, ignore_ko=False, allow_filling_in_own_eye=False): b = self.board # (x, y) must be on the board if not b.on_board(x, y): return False # (x, y) must be an empty intersection if b.color(x, y) != EMPTY: return False # determine if (x, y) is in ko captured_chains = self.captured_chains(x, y) if not ignore_ko and len(captured_chains) == 1 \ and b.chain_size_(captured_chains[0]) == 1 \ and self.previous_captured_size == 1 \ and b.find(self.previous_x, self.previous_y) == \ captured_chains[0]: return False # the play is illegal if it causes suicide without capturing # any opponent's stones if self.suicide(x, y) and len(captured_chains) == 0: return False # now the play is legal if filling in own eye is allowed if allow_filling_in_own_eye: return True # otherwise check if the play fills in own eye adjacent_chains = SmallSet(4) for dx, dy in _directions: if b.on_board(x + dx, y + dy): if b.color(x + dx, y + dy) != self.turn: return True else: adjacent_chains.add(b.find(x + dx, y + dy)) return len(adjacent_chains) != 1
def place(self, x, y, color): # create a stone chain of size 1 on (x, y) z = x * self.board_size + y self._color[z] = color self._parent[z] = z self._chain_size[z] = 1 if self._liberties[z] is None: self._liberties[z] = BitSet(self.board_size * self.board_size) for dx, dy in _directions: if self.on_board(x + dx, y + dy) \ and self.color(x + dx, y + dy) == EMPTY: self._liberties[z].add((x + dx) * self.board_size + y + dy) # update the liberty of the chains adjacent to (x, y) adjacent_chains = SmallSet(4) for dx, dy in _directions: if self.on_board(x + dx, y + dy) \ and self.color(x + dx, y + dy) != EMPTY: adjacent_chains.add(self.find(x + dx, y + dy)) for c in adjacent_chains: self._liberties[c].remove(z) # merge the stone on (x, y) with all the own stone chains that # are adjacent to (x, y) for dx, dy in _directions: if self.on_board(x + dx, y + dy) \ and self.color(x + dx, y + dy) == color: self._union(x, y, x + dx, y + dy) # remove all the opponent's stone chains that are captured for dx, dy in _directions: if self.on_board(x + dx, y + dy) \ and self.color(x + dx, y + dy) == -color \ and len(self.liberties(x + dx, y + dy)) == 0: self._remove(x + dx, y + dy)
def captured_chains(self, x, y): captured_chains = SmallSet(4) b = self.board for dx, dy in _directions: if b.on_board(x + dx, y + dy) \ and b.color(x + dx, y + dy) == -self.turn \ and len(b.liberties(x + dx, y + dy)) == 1: captured_chains.add(b.find(x + dx, y + dy)) return captured_chains
def ladder_escape(self, x, y): adjacent_chains = SmallSet(4) b = self.board for dx, dy in _directions: if b.on_board(x + dx, y + dy) \ and b.color(x + dx, y + dy) == self.turn \ and not adjacent_chains.contains(b.find(x + dx, y + dy)): if self.ladder_escape_(x, y, x + dx, y + dy): return True adjacent_chains.add(b.find(x + dx, y + dy)) return False
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