Implement ko checking

This commit is contained in:
Anna Rose 2012-04-14 02:26:17 -04:00
parent de81e9129f
commit 79d9fb0d9e

View File

@ -45,8 +45,6 @@ class Goban:
self.hover = None self.hover = None
# fixme - somewhere in this or its component functions we need to
# identify ko points :)
def play_move(self, pos): def play_move(self, pos):
rpos = self._real_pos(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 """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.""" 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 are we capturing
who = self._other_color(self.to_move) who = self._other_color(self.to_move)
captures = 0
for p in self._neighbors(pos): for p in self._neighbors(pos):
if not self._on_board(p): if not self._on_board(p):
continue continue
if not self._has_liberties(p, who): if not self._num_liberties(p, who):
captures += self._delete_group(p)
if self.to_move == Goban.BLACK: if self.to_move == Goban.BLACK:
self.black_captures += self._delete_group(p) self.black_captures += captures
elif self.to_move == Goban.WHITE: elif self.to_move == Goban.WHITE:
self.white_captures += self._delete_group(p) 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): if not self._on_board(pos):
return False return False
# Can't play atop another stone # Can't play atop another stone or on the ko point
if self.board[pos] != Goban.EMPTY: if self.board[pos] != Goban.EMPTY or pos == self.ko:
return False return False
# Temporarily place the stone # Temporarily place the stone
self.board[pos] = self.to_move 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) opponent = self._other_color(self.to_move)
kills_group = False kills_group = False
@ -100,7 +114,7 @@ class Goban:
if not self._on_board(d): if not self._on_board(d):
continue continue
if self._has_liberties(d, opponent) == 0: if self._num_liberties(d, opponent) == 0:
kills_group = True kills_group = True
break break
@ -112,19 +126,18 @@ class Goban:
# Recursively find whether there are liberties for the group # Recursively find whether there are liberties for the group
# at pos. Positive numbers are not necessarily accurate - # at pos.
# treat this as a boolean def _num_liberties(self, pos, who):
def _has_liberties(self, pos, who):
if not self._on_board(pos) or self.board[pos] != who: if not self._on_board(pos) or self.board[pos] != who:
return -1 return -1
bs = self.board_size * self.board_size bs = self.board_size * self.board_size
checked = [False] * bs 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]: if checked[pos]:
return 0 return 0
else: else:
@ -139,7 +152,7 @@ class Goban:
else: else:
liberties = 0 liberties = 0
for d in self._neighbors(pos): for d in self._neighbors(pos):
liberties += self._has_liberties_r(d, who, checked) liberties += self._num_liberties_r(d, who, checked)
return liberties return liberties
@ -147,8 +160,6 @@ class Goban:
# We don't need to worry about crossing ourselves with the # We don't need to worry about crossing ourselves with the
# recursion here, because we've already deleted the stone. # 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): def _delete_group(self, pos):
if not self._on_board(pos): if not self._on_board(pos):
return return
@ -205,6 +216,9 @@ class Goban:
if pos == self.last_move: if pos == self.last_move:
code = code + 'T' code = code + 'T'
if pos == self.ko:
code = code + 'C'
s = img_res[code] s = img_res[code]
s = pygame.transform.scale(s, (inc, inc)) s = pygame.transform.scale(s, (inc, inc))
ret.blit(s, ((pos % self.board_size) *inc, (pos / self.board_size) *inc)) ret.blit(s, ((pos % self.board_size) *inc, (pos / self.board_size) *inc))