Files @ b197a19e4afe
Branch filter:

Location: OneEye/src/go.py - annotation

Laman
chaining the states to a record
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
2fcaffe8cb70
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
2fcaffe8cb70
2fcaffe8cb70
b197a19e4afe
b197a19e4afe
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
b197a19e4afe
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
2fcaffe8cb70
0d57edb8be11
0d57edb8be11
0d57edb8be11
2fcaffe8cb70
0d57edb8be11
0d57edb8be11
0d57edb8be11
b197a19e4afe
b197a19e4afe
0d57edb8be11
0d57edb8be11
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
2fcaffe8cb70
0d57edb8be11
0d57edb8be11
2fcaffe8cb70
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
0d57edb8be11
2fcaffe8cb70
0d57edb8be11
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
0d57edb8be11
b197a19e4afe
0d57edb8be11
0d57edb8be11
0d57edb8be11
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
b197a19e4afe
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
2fcaffe8cb70
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)):
			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 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