Files @ 3cce3db6d4cc
Branch filter:

Location: OneEye/src/go.py

Laman
work on StateBag
import logging as log

from util import EMPTY,BLACK,WHITE,colorNames
from gamerecord import GameRecord


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",colorNames[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)):
			self._clearTemp()
			if not self._floodFill(-color,row+r,col+c): self._remove()

		# 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):
		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.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
		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

	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 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 transitionSequence(state1,state2,diff):
	return []


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