| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 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 |
|
|---|
| 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: |
|---|
| 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 = [] |
|---|
| 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) |
|---|
| 148 |
|
|---|
| 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' |
|---|
| 234 |
return |
|---|
| 235 |
|
|---|
| 236 |
numOfMatches = 0 |
|---|
| 237 |
numOfOccurrences = 0 |
|---|
| 238 |
|
|---|
| 239 |
|
|---|
| 240 |
|
|---|
| 241 |
|
|---|
| 242 |
|
|---|
| 243 |
|
|---|
| 244 |
|
|---|
| 245 |
|
|---|
| 246 |
|
|---|
| 247 |
|
|---|
| 248 |
|
|---|
| 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 |
|
|---|
| 262 |
|
|---|
| 263 |
|
|---|
| 264 |
|
|---|
| 265 |
|
|---|
| 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 |
|
|---|
| 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 |
|
|---|
| 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: |
|---|
| 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 |
|
|---|
| 307 |
ENDOFNODE = 128 |
|---|
| 308 |
BRANCHPOINT = 64 |
|---|
| 309 |
ENDOFVARIATION = 32 |
|---|
| 310 |
|
|---|
| 311 |
|
|---|
| 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') |
|---|
| 358 |
self.posTable = array('L') |
|---|
| 359 |
|
|---|
| 360 |
self.finalposC = array('B') |
|---|
| 361 |
|
|---|
| 362 |
|
|---|
| 363 |
def newgame_process(self): |
|---|
| 364 |
""" New game. """ |
|---|
| 365 |
self.movelist = array('B') |
|---|
| 366 |
self.fpC = array('B', [0] * 50) |
|---|
| 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' |
|---|
| 386 |
|
|---|
| 387 |
|
|---|
| 388 |
def endOfNode_process(self): |
|---|
| 389 |
if self.movelist: |
|---|
| 390 |
if self.movelist[-2] & ENDOFNODE: |
|---|
| 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: |
|---|
| 399 |
self.movelist.append(ENDOFNODE) |
|---|
| 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) |
|---|
| 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 |
|---|
| 526 |
|
|---|
| 527 |
if not self.retrieve_db(db.datapath): |
|---|
| 528 |
print "error" |
|---|
| 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 |
|
|---|
| 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 |
|---|
| 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 |
|
|---|
| 584 |
|
|---|
| 585 |
counter = 0 |
|---|
| 586 |
whereAreWe = [0] |
|---|
| 587 |
|
|---|
| 588 |
|
|---|
| 589 |
|
|---|
| 590 |
|
|---|
| 591 |
|
|---|
| 592 |
|
|---|
| 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 |
|
|---|
| 600 |
|
|---|
| 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 |
|
|---|
| 625 |
|
|---|
| 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 |
|
|---|
| 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 |
|---|
| 662 |
|
|---|
| 663 |
if switched and showColorSwap: |
|---|
| 664 |
|
|---|
| 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 |
|---|
| 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 |
|
|---|
| 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 |
|
|---|
| 696 |
|
|---|
| 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' |
|---|
| 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 |
|
|---|