Files
        @ 32221aadca28
    
        
              Branch filter: 
        
    Location: OneEye/src/go/core.py - annotation
        
            
            32221aadca28
            3.4 KiB
            text/x-python
        
        
    
    optimized board hashing: computing only hash diff
    | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | 3798475f45c1 3798475f45c1 32221aadca28 07f3fda1cbd7 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 07f3fda1cbd7 32221aadca28 32221aadca28 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 32221aadca28 3798475f45c1 32221aadca28 3798475f45c1 32221aadca28 32221aadca28 3798475f45c1 3798475f45c1 32221aadca28 07f3fda1cbd7 32221aadca28 3798475f45c1 0cb3fbe06b5d 07f3fda1cbd7 32221aadca28 32221aadca28 32221aadca28 3798475f45c1 32221aadca28 3798475f45c1 32221aadca28 3798475f45c1 3798475f45c1 077600f0c5f8 0cb3fbe06b5d f9ab2070bd69 07f3fda1cbd7 07f3fda1cbd7 f9ab2070bd69 f9ab2070bd69 f9ab2070bd69 f9ab2070bd69 f9ab2070bd69 f9ab2070bd69 077600f0c5f8 32221aadca28 07f3fda1cbd7 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 4e67f9e9f027 3798475f45c1 0cb3fbe06b5d 0cb3fbe06b5d 0cb3fbe06b5d 0cb3fbe06b5d 32221aadca28 32221aadca28 0cb3fbe06b5d 077600f0c5f8 32221aadca28 077600f0c5f8 07f3fda1cbd7 07f3fda1cbd7 07f3fda1cbd7 3798475f45c1 07f3fda1cbd7 a98de30efa51 32221aadca28 07f3fda1cbd7 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 | import logging as log
from util import EMPTY,BLACK,WHITE, colorNames,hashBoard,diffHash
from .helperboard import HelperBoard
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.toMove=BLACK
		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 self.board[r][c]!=EMPTY: return False
		self.board[r][c]=color
		self._freshHash^=diffHash(r,c,EMPTY,color)
		# capture neighbors
		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._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):
		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
 |