root/06/devel-old/aglPY.py

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

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

Line 
1 # File: agl.py
2
3 ##   Copyright (C) 2001-4 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 from searchPY import *
24 try:
25     from pattern import *
26 except ImportError:
27     print "Using patternPY"
28     from patternPY import *
29 from algosPY import Hit
30
31 class abstractGameList:
32     """
33     This class holds a list of games; more precisely, self.DBlist['9'], self.DBlist['13'] and
34     self.DBlist['19'] are lists of SGFDatabase's, (corresponding to board size 9, 13, 19 resp.)
35     and that is where the actual information which games are currently in the list is stored.
36
37     The class is initialized by passing the boardsize, and, optionally, a progress bar.
38     (The progress bar should be a class with callable redraw(x), write(text), clear()
39     methods. See the ProgressBar class in kombilo.py for details.)
40
41     The list of games can be sorted with respect to one of several criteria. The self.gameIndex
42     list points to the games in the right order, i.e. the n-th entry corresponds to the n-th
43     game. The entries of this list are pairs i,j; the corresponding game comes from the
44     SGFDatabase self.DBlist[`self.boardsize`][i], and is the game with index current[j] in
45     that database.
46
47     Methods:
48
49     readReferences       reads a 'references' file and stores the results in the self.references
50                          dictionary
51
52     getIndex             given an index n, this returns a pair i,j such that the n-th game in the
53                          complete list is given by self.DBlist[`self.boardsize`][i].current[j]
54     getFilename          returns complete filename, and gameNumber (since there may be more than one
55                          game stored in the SGF file)
56     getMoveno            returns position of first hit as given in
57                          the corresponding 'results' list (this is used to 'jump to match')
58                         
59     getString            returns the string which represents the corresponding game in the list of
60                          games as displayed in the Listbox; the format basically is:
61                          filename: white - black (result), date, hits
62     getAllStrings        returns a list of the strings as in getString for all games in the list
63     getGameInfo          returns the game info of the current game (i.e. the 'most relevant' entries
64                          of the root node of the game)
65     reset                resets the game list; afterwards the list contains all games from all
66                          databases which are not disabled
67     sortCrit             this returns, for an index n and a sort criterion, the value of the game
68                          n with respect to the criterion
69     updateGameIndex      updates the gameIndex: this looks at the db.current lists for db in
70                          self.DBlist to see which games are currently in the list, sorts them
71                          and builds the self.gameIndex list correspondingly. This method has to
72                          be invoked after all changes to the game list.
73     numOfGames           returns the number of games which are currently in the list
74     toggleDisabled       toggles the status of a database (given by the index in self.DBlist)
75                          between normal and disabled
76     saveData             returns the sgfpath's and datapath's of the databases in selfDBlist;
77                          this is called from the quit method in kombilo.py; this information is
78                          stored in the 'kombilo.def' file
79
80     addDB                with this method, directories can be added as SGFDatabases (possibly
81                          recursively); if necessary, the SGF files will first be 'process'-ed
82                          (see SGFDatabase.process)
83     addOneD              this method adds one database; it is called from addDB; probably there
84                          is no need to ever call this directly
85     removeDB             removes a database from self.DBlist, and also deletes the *.db files
86                          that belong to that database; if you don't want the *.db files to be deleted,
87                          but only temporarily disable the database, use toggleDisabled instead
88     reprocessDB          this reprocesses the database; this method should be called after making
89                          any changes to the SGF files in the database (such as adding or deleting
90                          files, or changing files)
91
92     loadDB               adds a database to self.DBlist; the *.db files for this database must have
93                          been created before
94
95     search               do a pattern search on the games currently in the list
96     doGISearch           do a game info search on the games currently in the list
97     doSigSearch          do a signature search on the games currently in the list
98     changeBoardSize
99     """
100
101     def __init__(self, boardsize = 19, progBar = None):
102         self.boardsize = boardsize
103         self.DBlist = {'9':[], '13':[], '19':[]}
104         self.Bwins = 0
105         self.Wwins = 0
106         self.Owins = 0 # others: Jigo, Void, Left unfinished, ? (Unknown)
107         self.references = {}
108         self.sort = None
109         self.gameIndex = []
110
111         self.showFilename = 1
112         self.showDate = 0
113
114         self.sortCriterion = (1, '', 0) # per database, by filename, not reversed
115
116         self.progBar = progBar
117
118
119     def readReferences(self, basepath):
120         """
121         Read the list of references to game commentaries from disk.
122         The references are stored in a dictionary self.references; the keys of this dictionary
123         are the symmetrized Dyer signatures of the games that have a reference; the value is
124         a string giving the actual references for that game.
125         """
126        
127         try:
128             ref_file = open(os.path.join(basepath, 'references'))
129             r_list = ref_file.readlines()
130             ref_file.close()
131         except IOError: return
132        
133         abbr = {}
134         found_abbr = 0
135         found_data = 0
136         for line in r_list:
137
138             if line and line[0] == '#': continue
139        
140             if not found_abbr:
141                 if line[:7] != '---Abbr': continue
142                 else:
143                     found_abbr = 1
144                     continue
145                
146             if found_abbr and not found_data:
147                 if strip(line):
148                     s = split(line)
149                     abbr[s[0]] = join(s[1:], ' ')
150
151             if found_abbr and not found_data:
152                 if line[:7] != '---Data': continue
153                 else:
154                     found_data = 1
155                     continue
156                
157             if found_data and strip(line):
158                 s = split(line)
159                 if abbr.has_key(s[1]):
160                     if self.references.has_key(s[0]):
161                         self.references[s[0]] += ', ' + abbr[s[1]] + ' ' + join(s[2:], ' ')
162                     else:
163                         self.references[s[0]] = abbr[s[1]] + ' ' + join(s[2:], ' ')
164
165
166     def getIndex(self, i):
167         """ Returns dbIndex, j, such that self.DBlist[`self.boardsize`][dbIndex].current[j]
168         corresponds to the i-th line of the (sorted) game list. """
169
170         if i < len(self.gameIndex):
171             return self.gameIndex[i]
172         else:
173             return -1, -1
174
175
176     def getFilename(self, index1):
177         DBindex, indexWithinCurrent = self.getIndex(index1)
178         index = self.DBlist[`self.boardsize`][DBindex].current[indexWithinCurrent]
179         return self.DBlist[`self.boardsize`][DBindex].getFilename(index)
180    
181
182     def getMoveno(self, index):
183         DBindex, indexWithinCurrent = self.getIndex(index)
184         try:
185             h = self.DBlist[`self.boardsize`][DBindex].results[indexWithinCurrent][0]
186             return h.moveno()
187         except IndexError: return [0]
188
189    
190     def getString(self, index):
191         DBindex, indexWithinCurrent = self.getIndex(index)
192
193         indexWithinDB = self.DBlist[`self.boardsize`][DBindex].current[indexWithinCurrent]
194
195         if DBindex == -1: return ''
196        
197         l = self.DBlist[`self.boardsize`][DBindex].namelist[indexWithinDB]
198         sl = []
199         try:
200             if self.references.has_key(l[4]): sl.append('* ')
201         except KeyError: pass
202         if self.showFilename: sl.append(l[0]+': ')
203         sl.append(l[2] + ' - ' + l[1] + ' (' + l[3] + '), ')
204         if self.showDate: sl.append(l[5]+', ')
205         try:
206             sl.append(', '.join([x.output() for x in self.DBlist[`self.boardsize`][DBindex].results[indexWithinCurrent]]))
207         except IndexError: pass
208         return join(sl, '')
209
210
211     def getAllStrings(self):
212         return [self.getString(i) for i in range(self.numOfGames())]
213
214
215     def getGameInfo(self, index):
216         DBindex, indexWithinCurrent = self.getIndex(index)
217         filename, gameNumber = self.getFilename(index)
218
219         try:
220             f = open(filename)
221             sgf = f.read()
222             f.close()
223             node = Cursor(sgf, 1).getRootNode(gameNumber)
224         except:
225             return
226
227         t = ''
228        
229         if node.has_key('PW'): t = t + node['PW'][0]
230         else: t = t + ' ?'
231         if node.has_key('WR'): t = t + ', ' + node['WR'][0]
232
233         t = t + ' - '
234
235         if node.has_key('PB'): t = t + node['PB'][0]
236         else: t = t + ' ?'
237         if node.has_key('BR'): t = t + ', ' + node['BR'][0]
238
239         if node.has_key('RE'): t = t + ', ' + node['RE'][0]
240         if node.has_key('KM'): t = t + ' (Komi ' + node['KM'][0] + ')'
241         if node.has_key('HA'): t = t + ' (Hcp ' + node['HA'][0] + ')'
242
243         t = t + '\n'
244
245         if node.has_key('EV'): t = t + node['EV'][0] + ', '
246         if node.has_key('RO'): t = t + node['RO'][0] + ', '
247         if node.has_key('DT'): t = t + node['DT'][0] + '\n'
248
249         if node.has_key('GC'):
250             gc = node['GC'][0]
251             gc = replace(gc, '\n\r', ' ')
252             gc = replace(gc, '\r\n', ' ')
253             gc = replace(gc, '\r', ' ')
254             gc = replace(gc, '\n', ' ')
255            
256             t = t + gc
257
258         try:
259             t2 = 'Commentary in ' +\
260                  self.references[self.DBlist[`self.boardsize`][DBindex].namelist[self.DBlist[`self.boardsize`][DBindex].current[indexWithinCurrent]][4]]
261         except: t2 = ''
262
263         return t, t2
264
265
266     def changeBoardSize(self, boardsize):
267         self.boardsize = boardsize
268         self.Bwins = 0
269         self.Wwins = 0
270         self.Owins = 0
271         self.references = {}
272         self.gameIndex = []
273         for db in self.DBlist[`self.boardsize`]:
274             success = db.loadDB()
275             if success != 1: print 'oops' # FIXME
276         self.updateGameIndex()
277        
278         for bs in [9, 13, 19]:
279             if bs != self.boardsize:
280                 for db in self.DBlist[`bs`]:
281                     db.clear()
282
283                
284     def reset(self):
285         """ Reset the list, s.t. it includes all the games from the (non-disabled) databases
286         in self.DBlist[`self.boardsize`]. """
287        
288         for i in range(len(self.DBlist[`self.boardsize`])):
289             db = self.DBlist[`self.boardsize`][i]
290             if not db.disabled:
291                 db.current = array('L', range(len(db.namelist)))
292                 db.results = [''] * len(db.namelist)
293             else:
294                 db.current = array('L')
295                 db.results = []
296         self.updateGameIndex()
297
298
299     def sortCrit(self, index, c):
300         dbIndex, j = self.getIndex(index)
301         return self.DBlist[`self.boardsize`][dbIndex].namelist[self.DBlist[`self.boardsize`][dbIndex].current[j]][c]
302
303
304     def updateGameIndex(self, sortList = 1):
305         """
306         Rebuild self.gameIndex from db.current for db in self.DBlist[`self.boardsize`], sort list
307         with respect to self.sortCriterion.
308         """
309        
310         self.gameIndex = []
311
312         perDB, criterion, reverse = self.sortCriterion
313         if criterion == 'PW': c = 2
314         elif criterion == 'PB': c = 1
315         elif criterion == 'DT': c = 5
316         else: c = 0          # criterion == 'filename'
317         
318         if (perDB and c == 0) or not sortlist:
319             for i in range(len(self.DBlist[`self.boardsize`])):
320                 db = self.DBlist[`self.boardsize`][i]
321                 if db.disabled or not db.namelist: continue
322                 self.gameIndex.extend([(i, j) for j in range(len(db.current))])
323         elif perDB:
324             for i in range(len(self.DBlist[`self.boardsize`])):
325                 db = self.DBlist[`self.boardsize`][i]
326                 if db.disabled or not db.namelist: continue
327                
328                 helpList = []
329                 helpList.extend([(db.namelist[j][c], j) for j in range(len(db.namelist))])
330                 helpList.sort()
331
332                 self.gameIndex.extend([(i, j) for dummy, j in helpList])
333         else:
334             srt = []
335             for i in range(len(self.DBlist[`self.boardsize`])):
336                 db = self.DBlist[`self.boardsize`][i]
337                 if db.disabled or not db.namelist: continue
338                 srt.extend([(db.namelist[j][c], i, j) for j in range(len(db.namelist))])
339
340             srt.sort()
341            
342             self.gameIndex = [(i, j) for dummy, i, j in srt]
343
344         if reverse and sortList: self.gameIndex.reverse()
345
346         self.Bwins, self.Wwins, self.Owins = 0, 0, 0
347         for db in self.DBlist[`self.boardsize`]:
348             if db.disabled or not db.namelist: continue
349             s = join([db.namelist[db.current[i]][3] for i in range(len(db.current))], '')
350             self.Bwins = count(s, 'B')
351             self.Wwins = count(s, 'W')
352             self.Owins = len(s) - self.Bwins - self.Wwins
353
354
355     def numOfGames(self):
356         num = 0
357         for db in self.DBlist[`self.boardsize`]:
358             if db.disabled or not db.namelist: continue
359             num += len(db.current)
360         return num
361
362
363     def toggleDisabled(self, DBIndex, disabled = 1):
364         self.DBlist[`self.boardsize`][DBIndex].disabled = disabled
365         self.updateGameIndex()
366
367
368     def saveData(self):
369         """
370         Return information about sgfpath and datapath of the db's in self.DBlist[`self.boardsize`];
371         this is called from the quit method in kombilo.py, in order to store this
372         data in the kombilo.def file.
373         """
374
375         result = []
376         for bs in ['19', '13', '9']:
377             for db in self.DBlist[bs]:
378                 if db.disabled:
379                     result.append('dD|%' + bs + '|%' + db.sgfpath + '|%' + db.datapath[0] +\
380                                   '|%' + db.datapath[1] +'\n')
381                 else:
382                     result.append('d|%' + bs + '|%' + db.sgfpath + '|%' + db.datapath[0] +\
383                                   '|%' + db.datapath[1] +'\n')       
384         return join(result, '')
385
386     # -------- SGF database management ---------------------------------------------------------
387
388     def addDB(self, sgfpath, datap, messages, recProcess=0,
389               algos=15, filenames='*.sgf', sloppySGF=1, duplicateCheck=1, strictDuplicateCheck=0):
390         # FIXME: algos default
391
392         self.stopAddDBVar = 0
393
394         if not recProcess:
395             self.addOneDB((messages, datap, algos, filenames,
396                            sloppySGF, duplicateCheck, strictDuplicateCheck), sgfpath, None)
397         else:
398             os.path.walk(sgfpath, self.addOneDB,
399                          (messages, datap, algos, filenames,
400                           sloppySGF, duplicateCheck, strictDuplicateCheck))
401
402        
403     def addOneDB(self, arguments, dbpath, dummy):        # dummys needed for os.path.walk
404         if self.stopAddDBVar: return
405
406         messages, datap, algos, filenames,\
407                   sloppySGF, duplicateCheck, strictDuplicateCheck = arguments
408         if datap[1] == '#': datap = (dbpath, '')
409         else:
410             try:
411                 flist = glob.glob(os.path.join(datap[0], 'namelist*.db'))
412                 i = 1
413                
414                 while 1:
415                     if not os.path.normpath(os.path.join(datap[0], 'namelist' + `i` + '.db')) in flist:
416                         break
417                     i += 1
418             except IOError:
419                 messages.insert('end', 'Error: ' + datap[0] + 'cannot be used as database path.\n')
420                 return
421
422             datap = (datap[0], `i`)
423
424         for db in self.DBlist[`self.boardsize`]:
425             if dbpath == db.sgfpath:
426                 messages.insert('end', 'Error: The database ' + dbpath + ' is already in the list.\n')
427                 return
428
429         newDB = SGFDatabase(self.boardsize, self.progBar)
430         if newDB.loadDB(dbpath, datap) != 1:
431             try:
432                
433                 newDB.process(dbpath, datap, algos, filenames, messages,
434                               sloppySGF, duplicateCheck, strictDuplicateCheck, 1) # self) FIXME
435             except IOError: # FIXME
436                 messages.insert('Error',
437                                 'A fatal error occured when processing ' + dbpath + '. Please send a bug report.\n')
438                 return
439
440         self.DBlist[`self.boardsize`].append(newDB)
441         self.updateGameIndex()
442         messages.insert('end', 'Added ' + dbpath + '.\n')
443
444
445     def removeDB(self, toBeRemoved, messages):
446         for ii in toBeRemoved:
447             i = int(ii)
448             datap = self.DBlist[`self.boardsize`][i].datapath
449             dbpath = self.DBlist[`self.boardsize`][i].sgfpath
450             filelist = []
451             for algo in ALL_ALGOS.keys():
452                 if self.DBlist[`self.boardsize`][i].algos & algo:
453                     filelist.extend(ALL_ALGOS[algo].filelist)
454             del self.DBlist[`self.boardsize`][i]
455
456             deletionFailed = 0
457             for filename in filelist:
458                 try:
459                     os.remove(os.path.join(datap[0], filename+datap[1]+'.db'))
460                 except:
461                     deletionFailed = 1
462             if deletionFailed: messages.insert('end', 'I/O Error: Could not delete the database files.\n')
463             messages.insert('end', 'Removed ' + dbpath + '\n')
464         self.updateGameIndex()   
465         self.reset()
466
467
468     def reprocessDB(self, toBeReprocessed, messages, algos=None, filenames='*.sgf',
469                     sloppySGF=1, duplicateCheck=1, strictDuplicateCheck=0):
470         failed = []
471         if not toBeReprocessed: return
472        
473         for i in toBeReprocessed:
474             if algos is None: thisAlgos = self.DBlist[`self.boardsize`][i].algos
475             else: thisAlgos = algos
476            
477             sgfpath = self.DBlist[`self.boardsize`][i].sgfpath
478             datap = self.DBlist[`self.boardsize`][i].datapath
479             filelist = []
480             for algo in ALL_ALGOS.keys():
481                 if self.DBlist[`self.boardsize`][i].algos & algo:
482                     filelist.extend(ALL_ALGOS[algo].filelist)
483             del self.DBlist[`self.boardsize`][i]
484
485             deletionFailed = 0
486             for filename in filelist:
487                 try:
488                     os.remove(os.path.join(datap[0], filename+datap[1]+'.db'))
489                 except:
490                     deletionFailed = 1
491             if deletionFailed:
492                 messages.insert('end','I/O Error: Could not delete the database files.\n')
493
494             newDB = SGFDatabase(self.boardsize, self.progBar)
495             try:
496                 newDB.process(sgfpath, datap, thisAlgos, filenames, messages,
497                               sloppySGF, duplicateCheck, strictDuplicateCheck, 1) # FIXME self)
498             except IOError: # FIXME
499                 messages.insert('end', 'Error: A fatal error occurred when processing ' + sgfpath + '.\n')
500                 failed.append(i)
501                 continue
502             try:
503                 file = open(os.path.join(datap[0], 'namelist'+datap[1]+'.db'))
504                 file.close()
505             except:
506                 self.reset()
507                 failed.append(i)
508                 continue
509            
510             self.DBlist[`self.boardsize`][i:i] = [newDB]
511
512         self.reset()
513         return failed
514
515
516     def loadDB(self, sgfpath, datapath, boardsize, disabled=0, sortList=0, insertWhere=-1):
517         newDB = SGFDatabase(boardsize, self.progBar)
518         if boardsize == self.boardsize:
519             success = newDB.loadDB(sgfpath, datapath, disabled)
520             if success != 1: return success
521         else:
522             newDB.sgfpath = sgfpath
523             newDB.datapath = datapath
524             newDB.disabled = disabled
525         if insertWhere == -1: self.DBlist[`boardsize`].append(newDB)
526         else: self.DBlist[`boardsize`][insertWhere:insertWhere] = [newDB]
527
528         self.updateGameIndex(sortList)
529         return 1
530
531     # --- searching -----------------------------------------------------------------
532
533     def search(self, pattern, options={}, sortLabelsByFrequency=0,
534                continuations={}, contLabels = None):
535         """
536         You can pass a pattern (an instance of the Pattern class) to this method,
537         and it will search for the pattern in the games of the gamelist. It will decide itself which
538         algorithms to use for the search.
539
540         FIXME: it should be possible to override the choice of algorithms
541
542         options        is a dictionary which will be passed on to the Algorithm classes performing
543                        the search
544         continuations
545         contLabels
546         """
547
548         if options.has_key('nextmove'): nextMoveVar = options['nextmove']
549         else: nextMoveVar = 0
550         if options.has_key('fixedcolor'): fixedColorVar = options['fixedcolor']
551         else: fixedColorVar = 0
552
553         print nextMoveVar
554         print "Build pattern list ..."
555         patternList = PatternList(pattern, fixedColorVar, nextMoveVar, self.boardsize)
556         print "done, size:", patternList.size() # FIXME
557         for p in range(patternList.size()):
558             patternList.get(p).printPattern()
559             if patternList.get(p).colorSwitch: print 'CS'
560
561         if contLabels is None:
562             contLabels = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L' ,'M' ,'N',
563                           'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'Y', 'Z', 'a', 'b', 'c', 'd',
564                           'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
565                           's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', '5', '6',
566                           '7', '8', '9']
567         contLabelsIndex = 0
568
569         numOfHits, Bwins, Wwins, numSwitched = 0, 0, 0, 0
570
571         startTime = time.time()
572         numOfGames = self.numOfGames()
573         doneSoFar = 0
574        
575         for db in self.DBlist[`self.boardsize`]:
576             if db.disabled or len(db.current)==0: continue
577             progStart = doneSoFar
578             progSpan = len(db.current)*1.0/numOfGames
579             progFirstInt = 0.3
580             doneSoFar += progSpan
581
582             a0 = Algo_hash_full_corners(self.boardsize)
583             if not a0.search(patternList, options, db, self.progBar,
584                              progStart, progStart+progFirstInt*progSpan):
585                 progFirstInt = 0.5
586                 a1 = Algo_finalpos(self.boardsize)
587                 a1.search(patternList, options, db, self.progBar,
588                           progStart, progStart+progFirstInt*progSpan)
589
590             intervals = 1
591
592             # if numOfStones in pattern is large (in proportion to pattern size), use intervals
593             # this is probably in particular useful for central patterns
594             # final tuning will have to be done with the C++ implementations
595
596             if intervals: # FIXME: decide which algo to use for second stage here
597                 a2 = Algo_intervals(self.boardsize)
598                 nh, bw, ww, sw = a2.search(patternList, options, db,
599                                            continuations, contLabelsIndex, contLabels,
600                                            self.progBar, progStart+progFirstInt*progSpan,
601                                            progStart+progSpan)
602
603             else:
604                 a2 = Algo_movelist(self.boardsize)
605                 nh, bw, ww, sw = a2.search(patternList, options, db,
606                                            continuations, contLabelsIndex, contLabels,
607                                            self.progBar, progStart+progFirstInt*progSpan, progStart+progSpan)
608
609             numOfHits += nh
610             Bwins += bw
611             Wwins += ww
612             numSwitched += sw
613
614         if sortLabelsByFrequency:
615             contList = [ (continuations[k]['B']+continuations[k]['W'], k) for k in continuations.keys()
616                          if continuations[k]['N'] != '?' ]
617             contList.sort()
618             contList.reverse()
619             contLabelsIndex = 0
620             t = []
621             for dummy, k in contList:
622                 t.append(continuations[k]['N'])
623                 continuations[k]['N'] = contLabels[contLabelsIndex]
624                 contLabelsIndex += 1
625             trans = maketrans(join(t, ''), join(contLabels[:contLabelsIndex], ''))
626             for db in self.DBlist[`self.boardsize`]:
627                 pass # FIXME ... results is now list of Hit's
628                 # s1 = join(db.results, '|%')
629                 # s2 = translate(s1, trans)
630                 # db.results = split(s2, '|%')
631
632         self.updateGameIndex()
633         if self.progBar: self.progBar.redraw(1.0)
634         if self.progBar: self.progBar.write('%1.1f seconds' % (time.time() - startTime))
635
636         return numOfHits, Bwins, Wwins, numSwitched, continuations
637    
638
639     def doGISearch(self, pbVar, pwVar, pVar, evVar, frVar, toVar, awVar, refVar,
640                    caseInsensitive):
641         """
642         Carry out the search for the parameters in *Var. All non-empty Var's
643         have to match at the same time. In the case of pbVar, pwVar, pVar, evVar,
644         the string has to occur somewhere in PB[...], PW[...], ..., not necessarily
645         at the beginning.
646
647         toVar and fromVar should be dates in the form YYYY-MM-DD; DD or (DD and MM)
648         can be omitted.
649         """
650
651         if not self.numOfGames(): return
652
653         if caseInsensitive:
654             pbVar = lower(pbVar)
655             pwVar = lower(pwVar)
656             pVar = lower(pVar)
657             evVar = lower(evVar)
658             awVar = lower(awVar)
659        
660         if frVar:
661             if not (re.match('\d\d\d\d-\d\d-\d\d', frVar) or re.match('\d\d\d\d-\d\d', frVar) \
662                     or re.match('\d\d\d\d', frVar)):
663                 frVar = ''
664
665         if toVar:
666             if re.match('\d\d\d\d-\d\d-\d\d', toVar) or re.match('\d\d\d\d-\d\d', toVar) or re.match('\d\d\d\d', toVar):
667                 toVar += 'Z'
668             else:
669                 toVar = ''
670
671         if not (pbVar or pwVar or pVar or evVar or frVar or toVar or awVar or refVar): return
672        
673         ctr = 0
674         currentTime = time.time()
675
676         oldfilename = ''
677
678         for DBIndex in range(len(self.DBlist[`self.boardsize`])):
679             db = self.DBlist[`self.boardsize`][DBIndex]
680            
681             index = 'start' # we can't do index = db.next() at the end of the loop, so we need this
682             
683             while 1:
684                 if index != 'start': index = db.next()
685                 else: index = db.start()
686                 if index is None: break
687
688                 g = db.namelist[index]
689
690                 ctr = ctr + 1
691                 if ctr % 10 == 0:
692                     if self.progBar: self.progBar.redraw(ctr*1.0/self.numOfGames())
693                
694                 if caseInsensitive:
695                     if pwVar and find(lower(g[2]), pwVar) == -1: continue
696                     if pbVar and find(lower(g[1]), pbVar) == -1: continue
697                     if pVar and find(lower(g[2]), pVar) == -1 \
698                        and find(lower(g[1]), pVar) == -1: continue
699                
700                     if frVar or toVar:
701                         date = g[5]
702                    
703                         if frVar and not date >= frVar: continue
704                         if toVar and not date <= toVar: continue
705
706                     if refVar and not self.references.has_key(g[4]): continue
707
708                     if evVar or awVar:
709                         filename, gameNumber = db.getFilename(index)
710
711                         if not oldfilename == filename:
712                             oldfilename = filename
713                             try:
714                                 file = open(filename)
715                                 sgf = file.read()
716                                 file.close()
717                             except IOError:
718                                 showwarning('IO Error', 'File not found: ' + filename)
719                                 continue
720                             newfile = 1
721                         else: newfile = 0
722                        
723                         if gameNumber == -1:         # no collection, so we get away without parsing the SGF file
724                             sgf = lower(sgf)
725                             if awVar and find(sgf, awVar) == -1: continue
726
727                             ok = 1
728                             for var,tag in [(evVar,'ev[')]:          # note that we have already lower'ed sgf!
729                                 if var:
730                                     v_begin = find(sgf, tag)
731                                     if v_begin == -1: ok = 0
732                                     else:
733                                         v_text = sgf[v_begin: find(sgf, ']', v_begin)]
734