Files
@ 9a3f61bf97f2
Branch filter:
Location: OneEye/src/go/engine.py - annotation
9a3f61bf97f2
3.3 KiB
text/x-python
StateBag: optimized a little, still slow
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | 7ef3360afbe5 077600f0c5f8 7f1984280936 7f1984280936 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 b06733513452 7f1984280936 7f1984280936 7f1984280936 077600f0c5f8 077600f0c5f8 7f1984280936 7f1984280936 4e67f9e9f027 4e67f9e9f027 4e67f9e9f027 4e67f9e9f027 f9ab2070bd69 4e67f9e9f027 7ef3360afbe5 7f1984280936 7f1984280936 0cb3fbe06b5d 4e67f9e9f027 077600f0c5f8 f9ab2070bd69 f9ab2070bd69 4e67f9e9f027 f9ab2070bd69 4e67f9e9f027 7f1984280936 4e67f9e9f027 4e67f9e9f027 4e67f9e9f027 4e67f9e9f027 4e67f9e9f027 f9ab2070bd69 f9ab2070bd69 f9ab2070bd69 7f1984280936 7f1984280936 f9ab2070bd69 f9ab2070bd69 f9ab2070bd69 f9ab2070bd69 f9ab2070bd69 f9ab2070bd69 7f1984280936 7f1984280936 077600f0c5f8 077600f0c5f8 4e67f9e9f027 7f1984280936 7f1984280936 7f1984280936 4e67f9e9f027 7f1984280936 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 b06733513452 7f1984280936 7f1984280936 7f1984280936 231b6d9b9561 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 231b6d9b9561 7f1984280936 7f1984280936 7f1984280936 7ef3360afbe5 7ef3360afbe5 7f1984280936 231b6d9b9561 b06733513452 b06733513452 b06733513452 b06733513452 7ef3360afbe5 b06733513452 7f1984280936 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7f1984280936 | from .core import PASS
from . import core
## Compute move sequence from state1 to state2.
#
# @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)
class SpecGo(core.Go):
def __init__(self,boardSize=19):
super().__init__(boardSize)
def listRelevantMoves(self,diff):
"""There can be 3 different changes in the diff: additions, deletions and replacements.
Additions can be taken as relevant right away.
Deletions and replacements had to be captured, so we add their liberties.
Also any non-missing stones of partially deleted (or replaced) groups had to be replayed, so add them too.
Needs to handle snapback, throw-in.
There's no end to what could be theoretically relevant, but such sequences are long and we will pretend they won't happen."""
res=({PASS},{PASS})
for d in diff:
(r,c,action,color)=d
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))
# 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]:
self._helper.clear()
self._helper.floodFill(color if action=="-" else 1-color, r, c)
res[colorKey].union(self._helper.getContinuousArea())
for (ri,ci) in self._helper.getContinuousArea():
res[colorKey].add((ri,ci))
res[1-colorKey].add((ri,ci))
for (rj,cj) in self.listNeighbours(ri,ci):
res[colorKey].add((rj,cj))
res[1-colorKey].add((rj,cj))
return res
def listNeighbours(self,r,c):
if r>0: yield (r-1,c)
if r+1<self.boardSize: yield (r+1,c)
if c>0: yield (r,c-1)
if c+1<self.boardSize: yield (r,c+1)
class Engine:
def __init__(self,g=None):
self._g=g or SpecGo()
self._moveList=(set(),set())
def load(self,state1,diff):
self._g.load(state1)
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,10,2):
seq=self.dfs(state2,i)
if seq:
seq.reverse()
return seq
return None
def dfs(self,state2,limit):
g=self._g
moveSet=self._moveList[(1-g.toMove)>>1]
for m in moveSet.copy():
if not g.doMove(g.toMove,*m): continue
captured=g.captures[:g.captureCount]
moveSet.remove(m)
if m==PASS: # no reason for both players to pass
self._moveList[(1-g.toMove)>>1].remove(m)
if limit>1:
seq=self.dfs(state2,limit-1)
if seq:
self._undoMove(m,captured)
seq.append((g.toMove,*m))
return seq
if limit==1 and g.hash()==state2.hash():
self._undoMove(m,captured)
return [(g.toMove,*m)]
self._undoMove(m,captured)
return None
def _undoMove(self,move,captured):
g=self._g
g.undoMove(*move,captured)
k=(1-g.toMove)>>1
self._moveList[k].add(move)
if move==PASS:
self._moveList[1-k].add(move)
eng=Engine()
|