Changeset - 2fcaffe8cb70
[Not reviewed]
default
0 2 2
Laman - 8 years ago 2017-04-17 20:28:13

checking for a legal position
4 files changed with 71 insertions and 9 deletions:
0 comments (0 inline, 0 general)
src/core.py
Show inline comments
 
import os
 
import multiprocessing
 
import threading
 
import logging as log
 
import PIL
 
from util import MsgQueue
 
from gui import gui
 
from imageanalyzer import ImageAnalyzer
 
from go import Go
 
from go import Go, isLegalPosition
 
import config as cfg
 

	
 

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

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

	
 
@@ -38,25 +38,28 @@ class Core:
 
		if tresW is not None: self.detector.tresW=tresW
 
		self.analyze()
 

	
 
	def relativeFrame(self,step):
 
		self._imgIndex=(self._imgIndex+step)%len(self._imgs)
 
		imgPath=os.path.join(self._imgDir,self._imgs[self._imgIndex])
 
		self._frame=PIL.Image.open(imgPath)
 
		self._guiMessages.send("setCurrentFrame",(self._frame.copy(),))
 
		self.analyze()
 

	
 
	def analyze(self):
 
		if self.detector.analyze(self._frame):
 
			self._guiMessages.send("setGameState",(self.detector.board,))
 
			if isLegalPosition(self.detector.board):
 
				self._guiMessages.send("setGameState",(self.detector.board,))
 
			else:
 
				log.info("illegal position detected")
 

	
 
	def listen(self):
 
		listenerThread=threading.Thread(target=lambda: self._ownMessages.listen())
 
		listenerThread.start()
 

	
 
	def joinGui(self):
 
		self._guiProc.join()
 
		self._ownMessages.send("!kill")
 

	
 
	def _handleEvent(self,e):
 
		actions={
 
			"setCorners": self.setCorners,
src/go.py
Show inline comments
 
EMPTY=0
 
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]*self.boardSize for x in range(boardSize)]
 
		self.board=[[EMPTY]*boardSize for x in range(boardSize)]
 
		self._temp=[[EMPTY]*boardSize for x in range(boardSize)]
 

	
 
	## 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 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)):
 
			self.temp=[[False]*self.boardSize for x in self.board]
 
			self._clearTemp()
 
			if not self._floodFill(-color,row+r,col+c): self._remove()
 

	
 
		# check for suicide
 
		self.temp=[[False]*self.boardSize for x in self.board]
 
		self._clearTemp()
 
		if not self._floodFill(color,row,col):
 
			self.board[row][col]=EMPTY
 
			return False
 
		return True
 

	
 
	## 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):
 
		if col<0 or col>=self.boardSize or row<0 or row>=self.boardSize: return False # out of range
 
		if self.temp[row][col]: return False # already visited
 
		if self._temp[row][col]: return False # already visited
 
		if self.board[row][col]==EMPTY: return True # found a liberty
 
		if self.board[row][col]!=color: return False # opponent's stone
 
		self.temp[row][col]=True # set visited
 
		self._temp[row][col]=True # set visited
 
		return self._floodFill(color,row,col-1) or self._floodFill(color,row,col+1) or self._floodFill(color,row-1,col) or self._floodFill(color,row+1,col) # check neighbors
 

	
 
	## Removes stones at coordinates marked with True in self.temp.
 
	def _remove(self):
 
		for r in range(self.boardSize):
 
			for c in range(self.boardSize):
 
				if self.temp[r][c]: self.board[r][c]=EMPTY
 
				if self._temp[r][c]: self.board[r][c]=EMPTY
 

	
 
	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):
 
	boardSize=len(board)
 
	temp=[[None]*boardSize for x in range(boardSize)]
 

	
 
	for r in range(boardSize):
 
		for c in range(boardSize):
 
			if board[r][c]==EMPTY: continue
 
			if temp[r][c]: continue
 
			if not dfs([(r,c)],board,temp): return False
 
	return True
 

	
 

	
 
def dfs(stack,board,mask):
 
	boardSize=len(board)
 
	(r,c)=stack[0]
 
	color=board[r][c]
 

	
 
	while len(stack)>0:
 
		(r,c)=stack.pop()
 
		if board[r][c]==EMPTY: return True
 
		elif board[r][c]!=color: continue
 
		elif mask[r][c]: return True
 
		mask[r][c]=True
 
		for (x,y) in ((0,-1),(-1,0),(0,1),(1,0)):
 
			if 0<=r+y<boardSize and 0<=c+x<boardSize:
 
				stack.append((r+y,c+x))
 
	return False
src/tests/__init__.py
Show inline comments
 
new file 100644
src/tests/testGo.py
Show inline comments
 
new file 100644
 
from unittest import TestCase
 

	
 
from go import isLegalPosition
 

	
 

	
 
class TestLegal(TestCase):
 
	def testLegal(self):
 
		board=[
 
			[0,0,0,0,0],
 
			[1,1,1,1,1],
 
			[-1,-1,0,1,0],
 
			[0,0,0,-1,-1],
 
			[0,-1,0,-1,0]
 
		]
 
		self.assertTrue(isLegalPosition(board))
 

	
 
	def testIllegal(self):
 
		board=[
 
			[0,1,0,0,0],
 
			[1,-1,1,0,0],
 
			[0,1,0,0,0],
 
			[0,0,0,0,0],
 
			[0,0,0,0,0]
 
		]
 
		self.assertFalse(isLegalPosition(board))
0 comments (0 inline, 0 general)