diff --git a/src/go.py b/src/go.py --- a/src/go.py +++ b/src/go.py @@ -106,6 +106,10 @@ def transitionMove(state1,state2): return False +def transitionSequence(state1,state2,diff): + return [] + + def dfs(stack,board,mask): boardSize=len(board) (r,c)=stack[0] diff --git a/src/statebag.py b/src/statebag.py --- a/src/statebag.py +++ b/src/statebag.py @@ -1,6 +1,81 @@ +"""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 BLACK,WHITE,EMPTY +from go import transitionSequence + + +## Crude lower bound on edit distance between states. +def estimateDistance(diff): + 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 # ??? + + +class BoardState: + def __init__(self,board): + self._board=tuple(tuple(x for x in row) for row in board) + self.prev=None + self._next=None + self.moves=[] + self.weight=0 + self.diff2Prev=None + + 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,item2,item1)) # from->to + return res + + class StateBag: def __init__(self): self._states=[] def pushState(self,board): - self._states.append(board) + sn=BoardState(board) + for s in reversed(self._states): + diff=sn-s + distEst=estimateDistance(diff) + if distEst>3: continue # we couldn't find every such move sequence anyway without a clever algorithm + weightEst=s.weight-distEst + if weightEst<=sn.weight: continue + moves=transitionSequence(s,sn,diff) + weight=s.weight-len(moves) + if weight<=sn.weight: continue + sn.prev=s + sn.diff2Prev=diff + sn.moves=moves + sn.weight=weight + self._states.append(sn) + + def merge(self,branch): + pass