diff --git a/src/core.py b/src/core.py --- a/src/core.py +++ b/src/core.py @@ -7,6 +7,7 @@ from util import MsgQueue from gui import gui from imageanalyzer import ImageAnalyzer from go import Go, isLegalPosition +from statebag import StateBag import config as cfg @@ -15,6 +16,7 @@ class Core: self.grid=None self.go=Go() self.detector=ImageAnalyzer() + self.states=StateBag() self._ownMessages=MsgQueue(self._handleEvent) self._guiMessages=MsgQueue() @@ -48,7 +50,10 @@ class Core: def analyze(self): if self.detector.analyze(self._frame): if isLegalPosition(self.detector.board): + self.states.pushState(self.detector.board) self._guiMessages.send("setGameState",(self.detector.board,)) + + self.go.transitionMove(self.detector.board) else: log.info("illegal position detected") diff --git a/src/gamerecord.py b/src/gamerecord.py new file mode 100644 --- /dev/null +++ b/src/gamerecord.py @@ -0,0 +1,61 @@ +import logging as log + + +colorNames={1:"B",-1:"W"} + + +class Point: + def __init__(self,c,r): + self.r=r + self.c=c + + def __iter__(self): + yield self.c + yield self.r + + def __str__(self): + a=ord("a") + return chr(a+self.c)+chr(a+self.r) + + +class GameRecord: + def __init__(self): + self.playerB="" + self.playerW="" + self.root=Node() + self.currNode=self.root + + def move(self,color,row,col): + if color not in colorNames: + raise ValueError("invalid color: {0}".format(color)) + colorName=colorNames[color] + res=Node() + res.properties[colorName]=Point(col,row) + self.currNode.children.append(res) + self.currNode=res + return res + + def __str__(self): + res=["("] + stack=[self.root] + while len(stack)!=0: + v=stack.pop() + res.append(v) + if v==")": continue + if len(v.children)>1: + res.append("(") + stack.append(")") + stack.extend(reversed(v.children)) + res.append(")") + return "".join(str(x) for x in res) + + +class Node: + def __init__(self): + self.properties=dict() + self.parent=None + self.children=[] + + ## Returns textual representation of the Node itself, but disregards its children. + def __str__(self): + return ";" + "".join("{0}[{1}]".format(k,str(p)) for (k,p) in self.properties.items()) diff --git a/src/go.py b/src/go.py --- a/src/go.py +++ b/src/go.py @@ -1,3 +1,8 @@ +import logging as log + +from gamerecord import GameRecord + + EMPTY=0 BLACK=1 WHITE=-1 @@ -9,6 +14,8 @@ class Go: self.boardSize=boardSize self.board=[[EMPTY]*boardSize for x in range(boardSize)] self._temp=[[EMPTY]*boardSize for x in range(boardSize)] + self.toMove=BLACK + self._record=GameRecord() ## Executes a move. # @@ -17,6 +24,7 @@ class Go: # @param color BLACK or WHITE # @return True on success, False on failure (illegal move) def move(self,color,row,col): + if color!=self.toMove: log.warning("move by %s out of order",color) if self.board[row][col]!=EMPTY: return False self.board[row][col]=color @@ -31,8 +39,17 @@ class Go: if not self._floodFill(color,row,col): self.board[row][col]=EMPTY return False + self._record.move(color,row,col) + self.toMove=-1*color return True + def transitionMove(self,board): + res=transitionMove(self.board,board) + if not res: return res + (r,c,color)=res + return self.move(color,r,c) + + ## Checks for liberties of a stone at given coordinates. # # The stone's group is marked with True in self.temp, ready for capture if needed. Recursively called for stone's neighbors. @@ -57,6 +74,7 @@ class Go: for j in range(self.boardSize): self._temp[i][j]=EMPTY + def exportBoard(board): substitutions={EMPTY:".", BLACK:"X", WHITE:"O"} return "\n".join("".join(substitutions.get(x,"?") for x in row) for row in board) @@ -74,6 +92,24 @@ def isLegalPosition(board): return True +def transitionMove(state1,state2): + moves=[] + for (r,(row1,row2)) in enumerate(zip(state1,state2)): + for (c,(item1,item2)) in enumerate(zip(row1,row2)): + if item1==EMPTY and item2!=EMPTY: + moves.append((r,c,item2)) + + if len(moves)==0: + log.info("no new stone") + return None + elif len(moves)==1: + log.info("new stone: %s",moves[0]) + return moves[0] + else: + log.warning("too many new stones: %s",moves) + return False + + def dfs(stack,board,mask): boardSize=len(board) (r,c)=stack[0] diff --git a/src/statebag.py b/src/statebag.py new file mode 100644 --- /dev/null +++ b/src/statebag.py @@ -0,0 +1,6 @@ +class StateBag: + def __init__(self): + self._states=[] + + def pushState(self,board): + self._states.append(board)