# HG changeset patch
# User Laman
# Date 2017-11-29 12:17:09
# Node ID b197a19e4afe14c20ec43f1865ee7d144a10a64f
# Parent  2fcaffe8cb708aba3d370157caebea66b7c7421d

chaining the states to a record

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)