diff --git a/lib/goban.py b/lib/goban.py index bee3a2a..94cca51 100644 --- a/lib/goban.py +++ b/lib/goban.py @@ -45,8 +45,6 @@ class Goban: self.hover = None - # fixme - somewhere in this or its component functions we need to - # identify ko points :) def play_move(self, pos): rpos = self._real_pos(pos) @@ -66,18 +64,34 @@ class Goban: """Look for stones captured on the 4 sides of pos, remove them and increment capture counter. This pos must be a *real* position value, not an x,y tuple.""" + # If we get here, we've definitely played a move, + # clearing any existing ko point + self.ko = None + # Who are we capturing who = self._other_color(self.to_move) + captures = 0 + for p in self._neighbors(pos): if not self._on_board(p): continue - if not self._has_liberties(p, who): - if self.to_move == Goban.BLACK: - self.black_captures += self._delete_group(p) - elif self.to_move == Goban.WHITE: - self.white_captures += self._delete_group(p) + if not self._num_liberties(p, who): + captures += self._delete_group(p) + + if self.to_move == Goban.BLACK: + self.black_captures += captures + elif self.to_move == Goban.WHITE: + self.white_captures += captures + + # Check for ko + if captures == 1 and self._num_liberties(pos, self.to_move): + # find the empty point + for p in self._neighbors(pos): + if self.board[p] == Goban.EMPTY: + self.ko = p + break @@ -85,14 +99,14 @@ class Goban: if not self._on_board(pos): return False - # Can't play atop another stone - if self.board[pos] != Goban.EMPTY: + # Can't play atop another stone or on the ko point + if self.board[pos] != Goban.EMPTY or pos == self.ko: return False # Temporarily place the stone self.board[pos] = self.to_move - liberties = self._has_liberties(pos, self.to_move) + liberties = self._num_liberties(pos, self.to_move) opponent = self._other_color(self.to_move) kills_group = False @@ -100,7 +114,7 @@ class Goban: if not self._on_board(d): continue - if self._has_liberties(d, opponent) == 0: + if self._num_liberties(d, opponent) == 0: kills_group = True break @@ -112,19 +126,18 @@ class Goban: # Recursively find whether there are liberties for the group - # at pos. Positive numbers are not necessarily accurate - - # treat this as a boolean - def _has_liberties(self, pos, who): + # at pos. + def _num_liberties(self, pos, who): if not self._on_board(pos) or self.board[pos] != who: return -1 bs = self.board_size * self.board_size checked = [False] * bs - return self._has_liberties_r(pos, who, checked) + return self._num_liberties_r(pos, who, checked) - def _has_liberties_r(self, pos, who, checked=None): + def _num_liberties_r(self, pos, who, checked=None): if checked[pos]: return 0 else: @@ -139,7 +152,7 @@ class Goban: else: liberties = 0 for d in self._neighbors(pos): - liberties += self._has_liberties_r(d, who, checked) + liberties += self._num_liberties_r(d, who, checked) return liberties @@ -147,8 +160,6 @@ class Goban: # We don't need to worry about crossing ourselves with the # recursion here, because we've already deleted the stone. - # It would be more efficient to avoid going backwards, - # but a lot more complicated (see _has_liberties) def _delete_group(self, pos): if not self._on_board(pos): return @@ -205,6 +216,9 @@ class Goban: if pos == self.last_move: code = code + 'T' + if pos == self.ko: + code = code + 'C' + s = img_res[code] s = pygame.transform.scale(s, (inc, inc)) ret.blit(s, ((pos % self.board_size) *inc, (pos / self.board_size) *inc))