diff --git a/src/go/engine.py b/src/go/engine.py --- a/src/go/engine.py +++ b/src/go/engine.py @@ -1,4 +1,5 @@ from .core import PASS +from .transpositiontable import TranspositionTable from . import core @@ -7,8 +8,8 @@ from . import core # @param colorIn {BLACK,WHITE}: color to start the sequence # @param colorOut {BLACK,WHITE}: color to close the sequence def getTransitionSequence(state1,state2,colorIn,colorOut,diff): - eng.load(state1,diff) - return eng.iterativelyDeepen(state2,colorIn,colorOut) + eng.load(state1) + return eng.iterativelyDeepen(state2,diff,colorIn,colorOut) class SpecGo(core.Go): @@ -28,8 +29,9 @@ class SpecGo(core.Go): colorKey=(1-color)>>1 # {-1,1}->{1,0} if action!="-" and (r,c) not in res[colorKey]: res[colorKey].add((r,c)) - for (ri,ci) in self.listNeighbours(r,c): # in case a stone was played and captured. !! might want to add even more - res[1-colorKey].add((ri,ci)) + if action=="*": + for (ri,ci) in self.listNeighbours(r,c): # in case a stone was played and captured. !! might want to add even more + res[1-colorKey].add((ri,ci)) # this is rather sloppy but correct. the time will show if it is effective enough # just floodFill from the current intersection, add everything you find and also all the neighbours to be sure if action!="+" and (r,c) not in res[colorKey] and (r,c) not in res[1-colorKey]: @@ -52,28 +54,38 @@ class SpecGo(core.Go): class Engine: + """Class searching for move sequences changing one board state into another.""" def __init__(self,g=None): self._g=g or SpecGo() self._moveList=(set(),set()) + self._transpositions=TranspositionTable() - def load(self,state1,diff): + def load(self,state1): self._g.load(state1) + + def iterativelyDeepen(self,state2,diff,colorIn,colorOut): + """Search for a move sequence from the loaded state to state2. Tries progressively longer sequences.""" self._moveList=self._g.listRelevantMoves(diff) - - def iterativelyDeepen(self,state2,colorIn,colorOut): startDepth=1 if colorIn==colorOut else 2 self._g.toMove=colorIn - for i in range(startDepth,6,2): - seq=self.dfs(state2,i) + for i in range(startDepth,5,2): + seq=self._dfs(state2,i) if seq: seq.reverse() return seq return None - def dfs(self,state2,limit): + def _dfs(self,state2,limit): + """Search for a "limit" move sequence from the loaded state to state2.""" g=self._g moveSet=self._moveList[(1-g.toMove)>>1] + transKey=(g.hash()*state2.hash()*limit)&0xffffffff + + transSeq=self._transpositions.get(transKey) + if transSeq is not None: + return transSeq[:] if transSeq else transSeq + for m in moveSet.copy(): if not g.doMove(g.toMove,*m): continue captured=g.captures[:g.captureCount] @@ -82,11 +94,13 @@ class Engine: self._moveList[(1-g.toMove)>>1].remove(m) if limit>1: - seq=self.dfs(state2,limit-1) + seq=self._dfs(state2,limit-1) if seq: self._undoMove(m,captured) seq.append((g.toMove,*m)) + self._transpositions.put(transKey,seq[:]) return seq + else: self._transpositions.put(transKey,False) if limit==1 and g.hash()==state2.hash(): self._undoMove(m,captured)