root/06/devel-old/abstractBoardPY.py

Revision 166, 7.7 kB (checked in by ug, 4 years ago)

Subversion trouble ...

Line 
1 # file: board1.py
2
3 ##   This file is part of Kombilo, a go database program
4 ##   It contains classes implementing an abstract go board and a go
5 ##   board displayed on the screen.
6
7 ##   Copyright (C) 2001-4 Ulrich Goertz (u@g0ertz.de)
8
9 ##   This program is free software; you can redistribute it and/or modify
10 ##   it under the terms of the GNU General Public License as published by
11 ##   the Free Software Foundation; either version 2 of the License, or
12 ##   (at your option) any later version.
13
14 ##   This program is distributed in the hope that it will be useful,
15 ##   but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ##   GNU General Public License for more details.
18
19 ##   You should have received a copy of the GNU General Public License
20 ##   along with this program (gpl.txt); if not, write to the Free Software
21 ##   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 ##   The GNU GPL is also currently available at
23 ##   http://www.gnu.org/copyleft/gpl.html
24
25
26 from copy import deepcopy
27
28 class abstractBoard:
29     """ This class administrates a go board.
30         It keeps track of the stones currently on the board in the dictionary self.status,
31         and of the moves played so far in self.undostack
32
33         The "coordinate system" of the board is as follows:
34
35         (0,0) ------- (self.boardsize-1,0)
36           |                     |
37           |                     |
38         (0,bsize-1) --- (bsize-1, bsize-1)
39
40         (this is analogous to the SGF coordinate system).
41
42         It has methods to clear the board, play a stone, undo a move. """
43
44     def __init__(self, boardsize = 19):
45         self.status = {}
46         self.undostack = []
47         self.boardsize = boardsize
48
49     def getStatus(self, x, y):
50         if self.status.has_key((x, y)): return self.status[(x, y)]
51         else: return ' '
52
53     def setStatus(self, x, y, value):
54         self.status[(x, y)] = value
55
56
57     def copy(self):
58         ab = abstractBoard(self.boardsize)
59         ab.status = deepcopy(self.status)
60         ab.undostack = deepcopy(self.undostack)
61         return ab
62
63     def restore(self, ab):
64         self.boardsize = ab.boardsize
65         self.status = ab.status
66         self.undostack = ab.undostack
67
68     def len_cap_last(self):
69         """ Return number of captured stones in last move. """
70
71         return len(self.undostack[-1][2])
72
73     def get_cap_last(self):
74         return self.undostack[-1][2]
75
76     def undostack_pop(self):
77         return self.undostack.pop()
78
79     def undostack_append_pass(self):
80         self.undostack.append(((20,20), '' ,[]))
81
82     def len_undostack(self):
83         return len(self.undostack)
84
85     def neighbors(self,x):
86         """ Returns the coordinates of the 4 (resp. 3 resp. 2 at the side / in the corner) intersections
87             adjacent to the given one. """
88         if   x[0]== 0                :     l0 = [1]
89         elif x[0]== self.boardsize-1 :     l0 = [self.boardsize-2]
90         else:                              l0 = [x[0]-1, x[0]+1]
91
92         if   x[1]== 0                :     l1 = [1]
93         elif x[1]== self.boardsize-1 :     l1 = [self.boardsize-2]
94         else:                              l1 = [x[1]-1, x[1]+1]
95
96         l = []
97         for i in l0: l.append((i,x[1]))
98         for j in l1: l.append((x[0],j))
99
100         return l
101
102
103     def clear(self):
104         """ Clear the board """
105         self.status = {}
106         self.undostack=[]       
107
108
109
110
111     def play(self,pos,color):
112         """ This plays a color=black/white stone at pos, if that is a legal move
113             (disregarding ko), and deletes stones captured by that move.
114             It returns 1 if the move has been played, 0 if not. """
115
116         if color is None: color = self.currentColor
117         elif color in ['black', 'b']: color = 'B'
118         elif color in ['white', 'w']: color = 'W'
119
120         if self.getStatus(pos[0], pos[1]) != ' ':                # check if empty
121             return 0
122
123         l = self.legal(pos,color)
124         if l:                                       # legal move?
125             captures = l[1]
126             for x in captures: del self.status[x]   # remove captured stones, if any
127             self.undostack.append((pos,color,captures))   # remember move + captured stones for easy undo
128             return 1
129         else: return 0
130
131
132     def legal(self, pos, color):
133         """ Check if a play by color at pos would be a legal move. """
134         c = [] # captured stones
135         for x in self.neighbors(pos):
136             if self.getStatus(x[0],x[1])==self.invert(color):
137                 c = c + self.hasNoLibExcP(x, pos)       
138
139         self.status[pos]=color
140
141         if c:
142             captures = []
143             for x in c:
144                 if not x in captures: captures.append(x)
145             return (1, captures)
146
147         if self.hasNoLibExcP(pos):
148             del self.status[pos]
149             return ()
150         else: return (1, [])
151
152
153     def hasNoLibExcP(self, pos, exc = None):
154         """ This function checks if the string (=solidly connected) of stones containing
155             the stone at pos has a liberty (resp. has a liberty besides that at exc).
156             If no liberties are found, a list of all stones in the string is returned.
157
158             The algorithm is a non-recursive  implementation of a simple flood-filling:
159             starting from the stone at pos, the main while-loop looks at the intersections
160             directly adjacent to the stones found so far, for liberties or other stones that belong
161             to the string. Then it looks at the neighbors of those newly found stones, and so
162             on, until it finds a liberty, or until it doesn't find any new stones belonging
163             to the string, which means that there are no liberties.
164             Once a liberty is found, the function returns immediately. """
165            
166         st = []            # in the end, this list will contain all stones solidly connected to the
167                            # one at pos, if this string has no liberties
168         newlyFound = [pos] # in the while loop, we will look at the neighbors of stones in newlyFound
169         foundNew = 1
170        
171         while foundNew:
172             foundNew = 0
173             n = []         # this will contain the stones found in this iteration of the loop
174             for x in newlyFound:
175                 for y in self.neighbors(x):
176                     if self.getStatus(y[0], y[1]) == ' ' and y != exc:    # found a liberty
177                         return []
178                     elif self.getStatus(y[0],y[1])==self.getStatus(x[0],x[1]) \
179                          and not y in st and not y in newlyFound: # found another stone of same color
180                         n.append(y)
181                         foundNew = 1
182
183             st[:0] = newlyFound
184             newlyFound = n
185
186         return st     # no liberties found, return list of all stones connected to the original one
187
188
189     def undo(self, no=1):
190         """ Undo the last no moves. """
191         for i in range(no):
192             if self.undostack:
193                 pos, color, captures = self.undostack.pop()
194                 del self.status[pos]
195                 for p in captures: self.status[p] = self.invert(color)
196
197
198     def remove(self, pos, removeFromUndostack=0):
199         """Remove the stone at pos, if not removeFromUndostack, append this as capture to undostack.
200         Otherwise, find the placement of this stone in undostack, and remove it from there."""
201        
202         if removeFromUndostack:
203             for i in range(len(self.undostack), 0, -1):
204                 if pos == self.undostack[i-1][0]:
205                     self.undostack[i-1:i] = []
206         else:
207             self.undostack.append(((-1,-1), self.invert(self.status[pos]), [pos]))
208         del self.status[pos]
209
210
211     def invert(self,color):
212         if color == 'B': return 'W'
213         else:            return 'B'
Note: See TracBrowser for help on using the browser.