Files
@ 9a3f61bf97f2
Branch filter:
Location: OneEye/src/statebag.py - annotation
9a3f61bf97f2
4.7 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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 7ef3360afbe5 7ef3360afbe5 7f1984280936 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 7f1984280936 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 7f1984280936 3cce3db6d4cc 3cce3db6d4cc 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 9a3f61bf97f2 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 b06733513452 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 7ef3360afbe5 9a3f61bf97f2 32221aadca28 3cce3db6d4cc 9a3f61bf97f2 9a3f61bf97f2 7ef3360afbe5 7ef3360afbe5 b06733513452 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 7f1984280936 32221aadca28 32221aadca28 7f1984280936 f9ab2070bd69 f9ab2070bd69 f9ab2070bd69 f9ab2070bd69 f9ab2070bd69 f9ab2070bd69 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc 7f1984280936 3cce3db6d4cc 3cce3db6d4cc 7f1984280936 077600f0c5f8 7f1984280936 b06733513452 b06733513452 b06733513452 7ef3360afbe5 7ef3360afbe5 7ef3360afbe5 b06733513452 b06733513452 b06733513452 b06733513452 3cce3db6d4cc b197a19e4afe b197a19e4afe b197a19e4afe b197a19e4afe b197a19e4afe 3cce3db6d4cc b06733513452 b06733513452 9a3f61bf97f2 b06733513452 7f1984280936 9a3f61bf97f2 3cce3db6d4cc 9a3f61bf97f2 9a3f61bf97f2 3cce3db6d4cc b06733513452 3cce3db6d4cc 3cce3db6d4cc 3cce3db6d4cc | """Theory:
We have a sequence S of valid board states s_1, ..., s_n.
We search for a picked subsequence S_p of length k and m additional moves M such that:
- S_p augmented by M forms a valid sequence of moves
- k-m is maximal for S_p picked from S
It is relatively cheap to add new items to S.
User can change detector parameters, presumably in case the previous don't fit the (current) situation.
In such a case we assume the new parameters are correct from the current position onwards.
But the change might have been appropriate even earlier (before the user detected and fixed an error).
So we try to find the correct crossover point like this:
- construct a sequence S' = s'_i, ..., s'_n by reanalyzing the positions with a new set of parameters, where s_i is the point of previous user intervention or s_0
- for each s'_j:
- try to append it to S[:j]
- try to append it to S'[:j]
- remember the better variant
- linearize the fork back by discarding s'_j-s preceding the crossover and s_j-s following the crossover
"""
from util import EMPTY,BLACK,WHITE, hashBoard,exportBoard
from go.engine import getTransitionSequence
## Crude lower bound on edit distance between states.
def estimateDistance(diff):
# lot of room for improvements
additions=sum(1 for d in diff if d[2]=="+")
deletions=sum(1 for d in diff if d[2]=="-")
replacements=len(diff)-additions-deletions
if additions>0: return additions+replacements
elif replacements==0 and deletions>0: return 2 # take n, return 1
return replacements+1 # ???
## Update contents of diff1 by contents of diff2.
def updateDiff(diff1,diff2):
res=[]
i=j=0
m=len(diff1)
n=len(diff2)
while i<m and j<n:
(r1,c1,action1,item1)=diff1[i]
(r2,c2,action2,item2)=diff2[j]
if (r1,c1)==(r2,c2):
merged=transformSingle(action1,item1,action2,item2)
if merged: res.append((r1,c1,*merged))
i+=1
j+=1
elif (r1,c1)<(r2,c2):
res.append(diff1[i])
i+=1
else:
res.append(diff2[j])
j+=1
if i<m: res.extend(diff1[i:])
else: res.extend(diff2[j:])
return res
def transformSingle(action1,item1,action2,item2):
if action1=="+":
if action2!="-":
return ("+",item2)
elif action2=="-": return ("-",item2)
elif (action1=="*" and item1==item2) or (action1=="-" and item1!=item2): return ("*",item2)
return None
class GameTreeNode:
def __init__(self,parent,color):
self.parent=parent
self.color=color # color closing the move sequence
self.prev=None
self.moves=[] # move sequence from prev to self
self.weight=0
## Connect itself after v if it gains itself more weight.
def tryConnect(self,v,diff):
moves=getTransitionSequence(v.parent,self.parent,-1*v.color,self.color,diff)
if not moves: return
w=v.weight+2-len(moves) # proper one move transition increases the weight by 1
if w>self.weight:
self.moves=moves
self.prev=v
self.weight=w
class BoardState:
def __init__(self,board):
self._board=tuple(tuple(x for x in row) for row in board)
self.nodes=(GameTreeNode(self,BLACK),GameTreeNode(self,WHITE))
self.cachedDiff=[]
self._hash=None
def tryConnect(self,s,diff=None):
if diff is None: diff=self-s
distEst=estimateDistance(diff)
if distEst>3: return # we couldn't find every such move sequence anyway without a clever algorithm
weightEst=s.getWeight()+2-distEst
if weightEst<=self.getWeight(): return
for v1 in s.nodes:
for v2 in self.nodes:
v2.tryConnect(v1,diff)
def hash(self):
if self._hash is None: self._hash=hashBoard(self._board)
return self._hash
def export(self):
return exportBoard(self._board)
def exportDiff(self,s2):
return "vvv\n{0}\n=== {1} ===\n{2}\n^^^".format(self.export(), s2-self, s2.export())
def __iter__(self): return iter(self._board)
def __getitem__(self,key): return self._board[key]
def __sub__(self,x):
res=[]
for (r,(row1,row2)) in enumerate(zip(self._board,x)):
for (c,(item1,item2)) in enumerate(zip(row1,row2)):
if item1==item2: continue
elif item2==EMPTY: res.append((r,c,"+",item1))
elif item1==EMPTY: res.append((r,c,"-",item2))
else: res.append((r,c,"*",item1)) # ->to
return res
def __eq__(self,x):
return self.hash()==x.hash()
def setWeight(self,val):
for v in self.nodes: v.weight=val
def getWeight(self):
return max(v.weight for v in self.nodes)
def getPrev(self):
v=max(self.nodes, key=lambda v: v.weight)
return v.prev.parent if v.prev else None
class StateBag:
def __init__(self):
self._states=[]
def pushState(self,board):
sn=BoardState(board)
if len(self._states)>0:
if sn==self._states[-1]: return None # no change
sn.cachedDiff=sn-self._states[-1]
else: sn.setWeight(1)
diff=sn.cachedDiff
for s in reversed(self._states):
sn.tryConnect(s,diff)
diff=updateDiff(s.cachedDiff,diff)
self._states.append(sn)
return sn
def merge(self,branch):
pass
|