Changeset - b197a19e4afe
[Not reviewed]
default
0 2 2
Laman - 7 years ago 2017-11-29 12:17:09

chaining the states to a record
4 files changed with 108 insertions and 0 deletions:
0 comments (0 inline, 0 general)
src/core.py
Show inline comments
 
@@ -4,20 +4,22 @@ import threading
 
import logging as log
 
import PIL
 
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
 

	
 

	
 
class Core:
 
	def __init__(self):
 
		self.grid=None
 
		self.go=Go()
 
		self.detector=ImageAnalyzer()
 
		self.states=StateBag()
 

	
 
		self._ownMessages=MsgQueue(self._handleEvent)
 
		self._guiMessages=MsgQueue()
 

	
 
		self._imgDir=os.path.join(os.path.dirname(__file__), "..","images")
 
		self._imgs=sorted(os.listdir(self._imgDir))
 
@@ -45,13 +47,16 @@ class Core:
 
		self._guiMessages.send("setCurrentFrame",(self._frame.copy(),))
 
		self.analyze()
 

	
 
	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")
 

	
 
	def listen(self):
 
		listenerThread=threading.Thread(target=lambda: self._ownMessages.listen())
 
		listenerThread.start()
src/gamerecord.py
Show inline comments
 
new file 100644
 
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())
src/go.py
Show inline comments
 
import logging as log
 

	
 
from gamerecord import GameRecord
 

	
 

	
 
EMPTY=0
 
BLACK=1
 
WHITE=-1
 

	
 

	
 
class Go:
 
	## Initializes self.board to a list[r][c]=EMPTY.
 
	def __init__(self,boardSize=19):
 
		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.
 
	#
 
	#  Doesn't check for kos. Suicide not allowed.
 
	#
 
	#  @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
 

	
 
		# capture neighbors
 
		for r,c in ((-1,0),(1,0),(0,-1),(0,1)):
 
@@ -28,14 +36,23 @@ class Go:
 

	
 
		# check for suicide
 
		self._clearTemp()
 
		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.
 
	#
 
	#  @return True if alive, False if captured
 
	def _floodFill(self,color,row,col):
 
@@ -54,12 +71,13 @@ class Go:
 

	
 
	def _clearTemp(self):
 
		for i in range(self.boardSize):
 
			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)
 

	
 

	
 
def isLegalPosition(board):
 
@@ -71,12 +89,30 @@ def isLegalPosition(board):
 
			if board[r][c]==EMPTY: continue
 
			if temp[r][c]: continue
 
			if not dfs([(r,c)],board,temp): return False
 
	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]
 
	color=board[r][c]
 

	
 
	while len(stack)>0:
src/statebag.py
Show inline comments
 
new file 100644
 
class StateBag:
 
	def __init__(self):
 
		self._states=[]
 

	
 
	def pushState(self,board):
 
		self._states.append(board)
0 comments (0 inline, 0 general)