Files @ 7cb01d4080c9
Branch filter:

Location: OneEye/src/go/core.py

Laman
a hinted neural network (failed)
import logging

from util import EMPTY,BLACK,WHITE, colorNames,hashBoard,diffHash
from .helperboard import HelperBoard
from .gamerecord import GameRecord

log=logging.getLogger(__name__)

PASS=(99,99)


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.toMove=BLACK

		# utility field to allow undoing moves. but only useful right after doMove. this class doesn't need it, so it is not kept correct at other times
		self.captures=[None]*4
		self.captureCount=0

		self._helper=HelperBoard(self.board)
		self._freshHash=hashBoard(self.board) # always reflecting current state of the board
		self._hashes=[self._freshHash]
		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 doMove(self,color,r,c):
		if color!=self.toMove: log.warning("move by %s out of order",colorNames[color])
		if (r,c)==PASS:
			self.toMove*=-1 # pass
			self._hashes.append(self._freshHash)
			return True
		if self.board[r][c]!=EMPTY: return False

		self.board[r][c]=color
		self._freshHash^=diffHash(r,c,EMPTY,color)

		# capture neighbors
		self.captureCount=0
		for dr,dc in ((-1,0),(1,0),(0,-1),(0,1)):
			self._helper.clear()
			if not self._helper.floodFill(-color,r+dr,c+dc,EMPTY):
				self.captures[self.captureCount]=(r+dr,c+dc)
				self.captureCount+=1
				self._remove()

		# check for suicide and prevent it
		self._helper.clear()
		if not self._helper.floodFill(color,r,c,EMPTY):
			self.board[r][c]=EMPTY
			self._freshHash=self._hashes[-1]
			return False
		self._record.move(color,r,c)
		self.toMove=-1*color
		self._hashes.append(self._freshHash)
		return True

	def undoMove(self,r,c,captures):
		if (r,c)!=PASS:
			assert self.board[r][c]==-1*self.toMove, "{0}!={1}".format(self.board[r][c],-1*self.toMove)

			if len(captures)>0:
				self._helper.clear()
				for (ri,ci) in captures:
					self._helper.floodFill(EMPTY,ri,ci)
				self._fill(self.toMove)

			self.board[r][c]=EMPTY
		self.toMove*=-1
		self._hashes.pop()
		self._freshHash=self._hashes[-1]

	def transitionMove(self,board):
		res=transitionMove(self.board,board)
		if not res: return res
		(r,c,color)=res
		return self.doMove(color,r,c)

	def load(self,board):
		for (r,row) in enumerate(board):
			for (c,x) in enumerate(row):
				self.board[r][c]=x
		self._freshHash=hashBoard(self.board)
		self._hashes=[self._freshHash]

	def hash(self):
		return self._hashes[-1]

	## Removes stones at coordinates marked with True in self.helper.
	def _remove(self):
		self._fill(EMPTY)

	def _fill(self,filling):
		for (r,c) in self._helper.getContinuousArea():
			self._freshHash^=diffHash(r,c,self.board[r][c],filling)
			self.board[r][c]=filling


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