root/06/devel-old/algosPY.py

Revision 175, 74.9 kB (checked in by ug, 2 years ago)

Done some more work on pattern.cc; seems to work now.

Line 
1 # File: algosPY.py
2
3 ##   Copyright (C) 2001-6 Ulrich Goertz (u@g0ertz.de)
4
5 ##   This file is part of Kombilo 0.6, a go database program.
6
7 ##   This program is free software; you can redistribute it and/or modify
8 ##   it under the terms of the GNU General Public License as published by
9 ##   the Free Software Foundation; either version 2 of the License, or
10 ##   (at your option) any later version.
11
12 ##   This program is distributed in the hope that it will be useful,
13 ##   but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 ##   GNU General Public License for more details.
16
17 ##   You should have received a copy of the GNU General Public License
18 ##   along with this program (see doc/license.txt); if not, write to the Free Software
19 ##   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 ##   The GNU GPL is also currently available at
21 ##   http://www.gnu.org/copyleft/gpl.html
22
23 import os
24 from array import *
25 from string import lower, join
26 from copy import copy, deepcopy
27
28 class AlgoError(Exception): pass
29
30 def BW2XO(c):
31     try:
32         return {'B': 'X', 'W': 'O'}[c]
33     except:
34         return c
35
36
37
38 class Hit:
39     def __init__(self, counter, labelStr, where=None):
40         self.counter = counter
41         self.labelStr = labelStr
42         self.where = where
43
44     def moveno(self):
45         if self.where:
46             # print self.where
47             return self.where
48         else:
49             print 'no where'
50             if labelStr[-1]=='-':
51                 return [int(labelstr[:-1])]
52             else:
53                 return [int(labelStr)]
54
55     def output(self):
56         if not self.where: return '/' + self.labelStr
57         if len(self.where) == 1: return str(self.where[0]) + self.labelStr
58         m = 0
59         i = 0
60         while i < len(self.where):
61             if i%2: m += 1
62             else: m += self.where[i]
63             i += 1
64         return 'V' + str(m) + self.labelStr
65
66
67 class Algorithm: # virtual
68     """
69     This class is a prototype for search algorithms for Kombilo.
70
71     Two parts:
72
73      * processing: react to events during the processing
74
75      * search: do a search
76
77     Two types of algorithms (first-stage, second-stage):
78
79     The first type builds a list of candidates, for example by using hash tables,
80     or by looking at the final position of the game.
81
82     The second type checks which of the candidates are actual hits.
83     
84     ... FIXME
85     """
86
87     def __init__(self, boardsize): pass
88
89     def initialize_process(self):
90         """ Start of database. """
91         pass
92
93     def newgame_process(self):
94         """ New game. """
95         pass
96    
97     def AB_process(self, x, y): pass
98
99     def AW_process(self, x, y): pass
100
101     def AE_process(self, x, y, removed): pass
102
103     def endOfNode_process(self): pass
104
105     def B_process(self, counter, x, y, cap): pass
106
107     def W_process(self, counter, x, y, cap): pass
108
109     def branchpoint_process(self): pass
110
111     def endOfVariation_process(self): pass
112
113     def endgame_process(self): pass
114        
115     def finalize_process(self, datap):
116         """ End of database. """
117         pass
118
119     filelist = [] # List of the names of the files written to disk by this algorithm.
120
121     # ---------------------------------------------------------------
122
123     def search(self, patternList, options, gamelist, matchlist): pass
124
125 # ------------------------------------------------------------------------------------------
126
127 class Algo_finalpos(Algorithm):
128     """
129     This algorithm decides if the current game could possibly contain the search pattern,
130     by looking at the final position of the game. It then creates a list of candidates,
131     i.e. pairs describing an orientation and an anchor of the search pattern such that a
132     hit for that particular combination is possible in view of the final position.
133
134     The final position is stored in a bit-array, in order to reduce the number of comparison
135     needed to check for a search pattern.
136     """
137
138     def __init__(self, boardsize):
139         pass
140
141     def initialize_process(self, l):
142         """ Start of database. """
143         self.finalpos = array('B')
144
145     def newgame_process(self):
146         """ New game. """
147         self.fp = array('B', [255] * 100)   # '20x20x2 bit-array', in the end 00 means: B, W at some time,
148                                             #                                 01 = never B, but W ...
149
150     def AB_process(self, x, y):
151         self.fp[y/2 + 10*(x/2)] &= ~(1 << (2*(x%2 + 2*(y%2))))
152
153     def AW_process(self, x, y):
154         self.fp[y/2 + 10*(x/2)] &= ~(1 << (2*(x%2 + 2*(y%2))+1))
155
156     def AE_process(self, x, y, removed):
157         pass
158
159     def endOfNode_process(self):
160         pass
161
162     def B_process(self, x, y, cap):
163         self.fp[y/2 + 10*(x/2)] &= ~(1 << (2*(x%2 + 2*(y%2))))
164
165     def W_process(self, x, y, cap):
166         self.fp[y/2 + 10*(x/2)] &= ~(1 << (2*(x%2 + 2*(y%2))+1))
167
168     def branchpoint_process(self):
169         pass
170
171     def endOfVariation_process(self):
172         pass
173
174     def endgame_process(self):
175         self.finalpos.extend(self.fp)
176
177     def finalize_process(self, datap):
178         """ End of database. """
179
180         file = open(os.path.join(datap[0], 'finalpos'+ datap[1] + '.db'), 'wb')
181         self.finalpos.tofile(file)
182         file.close()
183
184     filelist = ['finalpos']
185
186     # ---------------------------------------------------------------
187
188     def duplicateCheck(self, sli, datapath=None):
189         if datapath is None:
190             if self.finalpos[sli*100:sli*100+100] == self.fp: return 1
191             else: return 0
192
193         if not self.currentDatap == datap:
194             self.currentDatap = datap
195             self.currentFP = array('B')
196             try:
197                 file = open(os.path.join(datap[0], 'finalpos'+datap[1]+'.db'), 'rb')
198                 while 1:
199                     try: self.currentFP.fromfile(file, 1000000)
200                     except EOFError: break
201                 file.close()
202             except: return 0
203            
204         if self.currentFP[sli*100:sli*100+100] == self.fp: return 1
205         else: return 0
206
207     # ---------------------------------------------------------------
208
209     def retrieve_db(self, datap):
210         try:
211             self.finalpos = array('B')
212             filelist = [(self.finalpos, 'finalpos')]
213             for var, filename in filelist:
214                 file = open(os.path.join(datap[0], filename+datap[1]+'.db'), 'rb')
215                 while 1:
216                     try: var.fromfile(file, 1000000)
217                     except EOFError: break
218                 file.close()
219             return 1
220         except IOError:
221             return 0
222
223
224     def search(self, patternList, options, db, progBar, progStart, progEnd):
225
226         print 'algo-finalpos start'
227         ctr = 0
228
229         possiblePatterns = range(patternList.size())
230         index = db.start()
231         counter = 0
232         if not self.retrieve_db(db.datapath):
233             print 'error' # FIXME
234             return
235
236         numOfMatches = 0
237         numOfOccurrences = 0
238
239         # print final position:
240
241         # for i in range(19):
242         #     for j in range(19):
243         #         if not (self.finalpos[index*100 + (i/2) + 10*(j/2)] & (1 << (2*(j%2 + 2*(i%2))))):
244         #             print 'X',
245         #         elif not (self.finalpos[index*100 + (i/2) + 10*(j/2)] & (1 << (2*(j%2 + 2*(i%2))+1))):
246         #             print 'O',
247         #         else: print '.',
248         #     print
249
250         while not index is None:
251
252             counter += 1
253             if progBar and not counter % 10:
254                 progBar.redraw((progEnd-progStart)*counter/len(db.current) + progStart)
255
256             matchList = []
257
258             for N in possiblePatterns:
259                        
260                 pattern = patternList.get(N)
261                 # print 'next pattern'
262                 # pattern.printPattern()
263                 # print
264
265                 # print 'N', N, pattern.left, pattern.right, pattern.top, pattern.bottom
266                 for a0 in range(pattern.left, pattern.right+1):
267                     for a1 in range(pattern.top, pattern.bottom+1):
268                         matches = 1
269
270                         # print 'a0', a0, 'a1', a1
271                                     
272                         pIndex = 2*(a1%2) + (a0%2)
273
274                         pbIndex = 0
275                         fpIndex = index*100 + a1/2 + (a0/2)*10
276
277                         for x in range(pattern.getBits(pIndex,0)):
278                             start = pattern.getBits(pIndex, pbIndex+1)
279                             length = pattern.getBits(pIndex, pbIndex+2)
280                             # print 's', pbIndex, start, length
281                             fpIndex += start
282                             pbIndex += 2
283                             for y in range(length):
284                                 pbIndex += 1
285                                 if (pattern.getBits(pIndex, pbIndex) & self.finalpos[fpIndex]):
286                                     matches = 0
287                                     break
288                                 fpIndex += 1
289                             if not matches: break
290                             fpIndex += 10 - start - length
291                            
292                         if matches:                       # found candidate
293                             matchList.append((N,(a0,a1)))
294
295            
296             if matchList:
297                 db.makeCurrentCandidate(matchList)
298                 numOfMatches += 1
299                 numOfOccurrences += len(matchList)
300             else: db.discardCurrent()
301
302             index = db.next()
303
304         print 'algo-finalpos:', numOfMatches, numOfOccurrences
305
306 # in x-coord:
307 ENDOFNODE = 128
308 BRANCHPOINT = 64          # these two flags ...
309 ENDOFVARIATION = 32       # ... are not combined with move coordinates
310
311 # in y-coord
312 REMOVE = 128
313 BLACK = 64
314 WHITE = 32
315
316 class Algo_movelist(Algorithm):
317     """
318     This algorithm stores the information from an SGF file in a "move list", that is a list
319     of black/white moves and captures, respectively. The coordinates of the move/capture are
320     appended (as two bytes) to the movelist, and the second coordinate gets a flag indicating
321     what happened at this positon (BLACK/WHITE/REMOVE).
322
323     In addition, if in the first byte the ENDOFNODE flag is set, this event is the last event
324     of the current node (usually there is of course just one "event" per node, namely the
325     placement of one stone, but if there are captures or AB/AW/AE tags, there may be several
326     "events" per node. Of course, the search engine should only look for hits after a full node
327     has been completed.
328
329     Finally, in order to handle variations, there are the BRANCHPOINT and ENDOFVARIATION flags.
330     They are stored without any move coordinates; nevertheless two bytes have to be appended ...
331     i.e. BRANCHPOINT and 0, or ENDOFVARIATION and 0.
332
333     BRANCHPOINT means that at this point several variations start. In practice this means that
334     all of the current information about expected hits etc. should be pushed on a stack
335     (self.branchpoints in the search method), such that we can jump back to this exact point
336     later.
337
338     ENDOFVARIATION means, as I suppose you guessed, the end of a variation; i.e. when encountering
339     an ENDOFVARIATION, we jump back to the state described by the topmost entry of the
340     self.branchpoints stack.
341
342     In addition to the move list, we store some information about captures: for each intersection,
343     we remember if at any point in the game a capture has occured at this position. This information
344     is useful when doing a search, because when a stone is placed which "destroys" a hit, and which
345     is not captured later, we don't have to follow the game any further to check for a hit at this
346     position.
347
348     For further information on how the search is actually carried out, see the search method.
349     """
350
351     def __init__(self, boardsize):
352         pass
353
354
355     def initialize_process(self, l):
356         """ Start of database. """
357         self.mainlistArr = array('B')    # here we store all the move lists of the current database
358         self.posTable = array('L')       # this is a table of pointers to the beginning of the
359                                          # corresponding move list
360         self.finalposC = array('B')      # here we store information about captures; this should be
361                                         
362
363     def newgame_process(self):
364         """ New game. """
365         self.movelist = array('B')
366         self.fpC = array('B', [0] * 50)     # '20x20 bit array', 0 = never captured, 1 = captured at some point
367         
368
369     def AB_process(self, x, y):
370         self.movelist.append(x)
371         self.movelist.append(y | BLACK)
372        
373
374     def AW_process(self, x, y):
375         self.movelist.append(x)
376         self.movelist.append(y | WHITE)
377
378
379     def AE_process(self, x, y, removed):
380         self.movelist.append(x)
381         if removed == 'B':
382             self.movelist.append(y | REMOVE | BLACK)
383         elif removed == 'W':
384             self.movelist.append(y | REMOVE | WHITE)
385         else: print 'oops' # FIXME
386
387
388     def endOfNode_process(self):
389         if self.movelist:
390             if self.movelist[-2] & ENDOFNODE: # there has been a pass or something, so we append an extra node
391                 self.movelist.append(ENDOFNODE)
392                 self.movelist.append(0)
393             else:
394                 self.movelist[-2] |= ENDOFNODE
395
396
397     def B_process(self, x, y, cap):
398         if not self.movelist:                  # the game starts here, so we put an ENDOFNODE here,
399             self.movelist.append(ENDOFNODE)    # to make sure that the empty board can be found
400             self.movelist.append(0)
401
402         self.movelist.append(x)
403         self.movelist.append(y | BLACK)
404
405         for x, y in cap:
406             self.movelist.append(x)
407             self.movelist.append(y | REMOVE | WHITE)
408             self.fpC[y/4 + 5*(x/2)] |= 1 << (x%2 + 2*(y%4))
409  
410                
411     def W_process(self, x, y, cap):
412         if not self.movelist:
413             self.movelist.append(ENDOFNODE)
414             self.movelist.append(0)
415        
416         self.movelist.append(x)
417         self.movelist.append(y | WHITE)
418
419         for x, y in cap:
420             self.movelist.append(x)
421             self.movelist.append(y | REMOVE | BLACK)
422             self.fpC[y/4 + 5*(x/2)] |= 1 << (x%2 + 2*(y%4))
423
424
425     def branchpoint_process(self):
426         self.movelist.append(BRANCHPOINT)
427         self.movelist.append(0)
428        
429
430     def endOfVariation_process(self):
431         self.movelist.append(ENDOFVARIATION)
432         self.movelist.append(0)
433
434
435     def endgame_process(self):
436         self.posTable.append(len(self.mainlistArr))
437         self.mainlistArr.append(255)                  # FIXME: this delimiter shouldn't be needed
438         self.mainlistArr.append(255)
439         self.mainlistArr.extend(self.movelist)
440
441         self.finalposC.extend(self.fpC)
442
443
444     def finalize_process(self, datap):
445         """ End of database. """
446        
447         filelist = [(self.posTable, 'posTable'), (self.mainlistArr, 'lists'),
448                     (self.finalposC, 'finalposC')]
449         for var, filename in filelist:
450             file = open(os.path.join(datap[0], filename + datap[1] + '.db'), 'wb')
451             var.tofile(file)
452             file.close()
453
454
455     filelist = ['posTable', 'lists', 'finalposC']
456
457     # ---------------------------------------------------------------
458
459     def retrieve_db(self, datap):
460         try:
461             self.posTable = array('L')
462             self.mainlistArr = array('B')
463             self.finalposC = array('B')
464             filelist = [(self.mainlistArr, 'lists'), (self.posTable, 'posTable'),
465                         (self.finalposC, 'finalposC')]
466             for var, filename in filelist:
467                 file = open(os.path.join(datap[0], filename+datap[1]+'.db'), 'rb')
468                 while 1:
469                     try: var.fromfile(file, 1000000)
470                     except EOFError: break
471                 file.close()
472             return 1
473         except IOError:
474             return 0
475            
476
477     def search(self, patternList, options, db,
478                continuations, contLabelsIndex, contLabels,
479                progBar, progStart, progEnd):
480
481         """
482         Here we do the actual search. "Matchlists" have to be available from some "first-stage search".
483         For each game, we proceed as follows: for each entry in the matchlist, we create a dictionary
484         which pattern.sizeX * pattern.sizeY entries, which tell us if at position (i,j) the current
485         board position coincides with the search pattern. More precisely, d[(i,j)] is 'X' if the search
486         pattern has a black stone at (i,j), but the current board does not, it is 'O' if the search
487         pattern has a white stone, but the current position has not, it is '.' if the search pattern
488         has an empty intersection, but the current board has not, and it is '*' if the search pattern
489         has a wildcard. (So initializing these dictionaries just means putting all stones in the
490         search pattern in them, because on the empty board, they all are missing.)
491
492         Then we go through the movelist, and for each entry we update the dictionaries correspondingly.
493         When reaching an ENDOFNODE, we check if there are any empty dictionaries; If there are, we have
494         found actual hits.
495
496         This basic procedure is complicated (but not very much) by three things:
497         - after finding a hit, we still are interested in the continuation
498           thus we do not directly append the hit to the list of results and forget about it, but instead
499           we create a 'found' entry in the dictionary which stores the move number when the hit was found.
500           We then look for the continuation as we proceed to go through the move list.
501         - possibly, we are interested in a move sequence, not just a pattern. In that case, we proceed as
502           described above in order to find the initial position, and - after finding it - we set a
503           'foundInitial' entry in the dictionary. Further moves in the corresponding region will only
504           be accepted for this potential hit if they are in the right order, as prescribed by the contList,
505           a list containing the information about the move sequence following the initial pattern.
506           If we reach the end of the contList, we have indeed found a hit, and can set the 'found' entry
507           (and look for the continuation play, as we go further through the move list.)
508         - We also may want to search in variations. To this end, we use the information about branchpoints
509           and end-of-variation points stored in the move list. At a branchpoint, we make a copy of all the
510           dictionaries, and contLists, and push them in the self.branchpoints stack. When reaching the end
511           of a variation, we reset the dictionaries etc. to the values stored in the topmost entry of the
512           stack.
513         """
514        
515         numOfHits = 0
516         self.numOfSwitched = 0
517         Bwins = 0
518         Wwins = 0
519
520         if options.has_key('movelimit'): self.movelimit = options['movelimit']
521         else: self.movelimit = 1000
522         if options.has_key('showcolorswap'): showColorSwap = options['showcolorswap']
523         else: showColorSwap = 1
524
525         searchInVariations = 1 # FIXME
526
527         if not self.retrieve_db(db.datapath):
528             print "error" # FIXME
529             return
530            
531         index = db.start()
532         gameCounter = 0
533
534         while not index is None:
535
536             gameCounter += 1
537             if progBar and not gameCounter % 10:
538                 progBar.redraw((progEnd-progStart)*gameCounter/len(db.current) + progStart)
539            
540             result = []
541             numOfSwitched = 0
542             self.branchpoints = []
543
544             movelist = self.mainlistArr
545             movelistIndex = self.posTable[index] + 2
546
547             endMovelist = movelistIndex
548             while endMovelist < len(self.mainlistArr) and self.mainlistArr[endMovelist] != 255:
549                 endMovelist += 2
550        
551             dicts = {}
552             contList = {}
553             contListIndex = {}
554             dictsNO = {}
555             Xinterv = {}
556             Yinterv = {}
557
558             # initialize dictionaries
559             
560             for m in db.getCurrentMatchlist():
561                 d = {}
562                 dNO = 0
563                 p = patternList.get(m[0])
564                 for i in range(p.sizeX):
565                     for j in range(p.sizeY):
566                         if p.getInitial(i, j) != '.':
567                             d[(i+m[1][0],j+m[1][1])] = p.getInitial(i,j)
568                         if p.getInitial(i,j) in ['X', 'O']:
569                             dNO += 1
570
571                 dicts[m] = d
572                 dictsNO[m] = dNO # no of places that are currently not correct
573
574                 contList[m] = []
575                 cL = p.contList.split('/')
576                 for t in cL:
577                     if t: contList[m].append((ord(t[0])+m[1][0], ord(t[1])+m[1][1], BW2XO(t[2])))
578
579                 contListIndex[m] = 0
580                 Xinterv[m] = (m[1][0], m[1][0] + patternList.get(m[0]).sizeX)
581                 Yinterv[m] = (m[1][1], m[1][1] + patternList.get(m[0]).sizeY)
582
583             # go through the game
584
585             counter = 0 # counts at which move in the game we are
586             whereAreWe = [0] # records the exact position in the game tree
587                              # format: list [n1, m1, n2, m2, n3 ..., ni], where
588                              # the current position can be reached by
589                              # going forward n1 times, then choosing the m1-th variation,
590                              # then going forward n2 times, then choosing the m2-th variation,
591                              # etc.
592                              # we have counter == sum_j nj // FIXME this cannot be correct (maybe i + sum_j nj)
593             
594             while movelistIndex < endMovelist and (counter <= self.movelimit or self.branchpoints):
595                
596                 if movelist[movelistIndex] & BRANCHPOINT:
597                     whereAreWeCopy = copy(whereAreWe)
598                     if movelistIndex < self.posTable[index]+4 or not movelist[movelistIndex-2] & ENDOFVARIATION:
599                         # whereAreWe[-1] -= 1
600                         # print 'X', counter
601                         whereAreWeCopy[-1] -= 1
602                         whereAreWeCopy.extend([0,0])
603                     self.branchpoints.append((deepcopy(dicts), deepcopy(dictsNO),
604                                               deepcopy(contList), deepcopy(contListIndex), counter,
605                                               whereAreWeCopy))
606                     movelistIndex += 2
607                     continue
608                 elif movelist[movelistIndex] & ENDOFVARIATION:
609                     if not patternList.nextMove:
610                         for m in dicts.keys():
611                             if dicts[m].has_key('found') and dicts[m]['found'] <= self.movelimit:
612                                 if patternList.get(m[0]).colorSwitch: numOfSwitched += 1
613                                 if patternList.get(m[0]).colorSwitch and showColorSwap:
614                                     if searchInVariations:
615                                         result.append(Hit(dicts[m]['found'], '-', dicts[m]['whereAreWe']))
616                                     else: result.append(Hit(dicts[m]['found'], '-'))
617                                 else:
618                                     if searchInVariations:
619                                         result.append(Hit(dicts[m]['found'], '', dicts[m]['whereAreWe']))
620                                     else: result.append(Hit(dicts[m]['found'], ''))
621                     dicts, dictsNO, contList, contListIndex, counter, whereAreWe = self.branchpoints.pop()
622                    
623                     whereAreWe[-2] += 1
624                     # print 'Y'
625                     # assert whereAreWe[-1] == 0
626                         
627                     movelistIndex += 2
628                     continue
629
630                 endOfNode = movelist[movelistIndex] & ENDOFNODE
631
632                 x = movelist[movelistIndex] & 31
633                 y = movelist[movelistIndex+1] & 31
634
635                 if (not movelist[movelistIndex+1] & REMOVE) \
636                    and movelist[movelistIndex+1] & (BLACK | WHITE):
637
638                     if movelist[movelistIndex+1] & BLACK:
639                         co, invco = 'X', 'O'   
640                     else: co, invco = 'O', 'X' 
641                    
642                     for m in dicts.keys():
643                         if Xinterv[m][0] <= x < Xinterv[m][1] and Yinterv[m][0] <= y < Yinterv[m][1]:
644
645                             if dicts[m].has_key('found') and dicts[m]['found'] <= self.movelimit:
646                                 # so (x,y) is the continuation
647
648                                 hit, label, contLabelsIndex, switched =\
649                                      patternList.updateContinuations(m[0], x, y, invco,
650                                                                      Xinterv[m][0], Xinterv[m][1],
651                                                                      Yinterv[m][0], Yinterv[m][1],
652                                                                      dicts[m]['found'], counter,
653                                                                      continuations, ''.join(contLabels),
654                                                                      contLabelsIndex,
655                                                                      db.getCurrentWinner())
656                                 if not hit:
657                                     del dicts[m]
658                                     del dictsNO[m]
659                                     continue
660
661                                 numOfSwitched += switched # FIXME: CHECK, was: patternList[m[0]].colorSwitch
662
663                                 if switched and showColorSwap:
664                                     # FIXME: CHECK,was: (patternList[m[0]].colorSwitch or colorSwitch) and showColorSwap:
665                                     if searchInVariations:
666                                         result.append(Hit(dicts[m]['found'], label+'-', dicts[m]['whereAreWe']))
667                                     else: result.append(Hit(dicts[m]['found'], label+'-'))
668                                 else:
669                                     if searchInVariations:
670                                         result.append(Hit(dicts[m]['found'], label, dicts[m]['whereAreWe']))
671                                     else: result.append(Hit(dicts[m]['found'], label))
672                                 del dicts[m]
673                                 del dictsNO[m]
674                                 continue     # with for m in dicts.keys()
675
676                             elif dicts[m].has_key('foundInitial'):
677                                 print contList[m], contListIndex[m]
678                                 if (x, y, co) == contList[m][contListIndex[m]]:
679                                     contListIndex[m] += 1
680                                     if contListIndex[m] == len(contList[m]):
681                                         dicts[m]['found'] = counter
682                                         dicts[m]['whereAreWe'] = copy(whereAreWe)
683                                         # print 'found', counter, whereAreWe
684                                         del dicts[m]['foundInitial']
685                                 else:
686                                     if dicts[m].has_key('dontRestore'):
687                                         del dicts[m]
688                                         continue
689                                     else:
690                                         contListIndex[m] = 0
691                                         del dicts[m]['foundInitial']
692                    
693                             if not dicts[m].has_key((x,y)):
694                                 if not (self.finalposC[index*50 + y/4 + 5*(x/2)] & (1 << (x%2 + 2*(y%4)))):
695                                                                   # this stone won't be captured later,
696                                                                   # so this cannot become a match
697                                     if not contListIndex[m]:
698                                         del dicts[m]
699                                         continue
700                                     else:
701                                         dicts[m]['dontRestore'] = 1
702                                 else:
703                                     dicts[m][(x,y)] = '.'
704                                     dictsNO[m] = dictsNO[m] + 1
705                             elif dicts[m][(x,y)] == lower(invco):
706                                 if not (self.finalposC[index*50 + y/4 + 5*(x/2)] & (1 << (x%2 + 2*(y%4)))):
707                                     if not contListIndex[m]:
708                                         del dicts[m]
709                                         continue
710                                     else:
711                                         dicts[m]['dontRestore'] = 1
712                                 else:
713                                     dictsNO[m] = dictsNO[m] + 1
714                            
715                             elif dicts[m][(x,y)] == co:
716                                 del dicts[m][(x,y)]
717                                 dictsNO[m] = dictsNO[m] - 1
718
719                 elif movelist[movelistIndex+1] & REMOVE:
720
721                     if movelist[movelistIndex+1] & BLACK:
722                         co = 'X'
723                         invco = 'O'
724                     elif movelist[movelistIndex+1] & WHITE:
725                         co = 'O'
726                         invco = 'X'
727                     else: print 'oops' # FIXME
728
729                     for m in dicts.keys():
730                         if not dicts[m].has_key('found'):
731
732                             if Xinterv[m][0] <= x < Xinterv[m][1] \
733