Changeset - 0cb3fbe06b5d
[Not reviewed]
default
0 5 1
Laman - 7 years ago 2017-12-13 13:41:29

Go, Engine: some tests and numerous bugfixes
6 files changed with 89 insertions and 17 deletions:
0 comments (0 inline, 0 general)
src/go/core.py
Show inline comments
 
@@ -32,24 +32,24 @@ class Go:
 

	
 
		# capture neighbors
 
		for r,c in ((-1,0),(1,0),(0,-1),(0,1)):
 
			self._helper.clear()
 
			if not self._helper.floodFill(-color,row+r,col+c,EMPTY): self._remove()
 

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

	
 
	def undoMove(self,r,c,captures):
 
		assert self.board[r][c]==-1*self.toMove
 
		assert self.board[r][c]==-1*self.toMove, "{0}!={1}".format(self.board[r][c],-1*self.toMove)
 
		self.toMove*=-1
 
		self.board[r][c]=self.toMove
 
		if len(captures)>0:
 
			self._helper.clear()
 
			for (r,c) in captures:
 
				self._helper.floodFill(EMPTY,r,c)
 
@@ -59,12 +59,17 @@ class Go:
 
	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
 

	
 
	def hash(self):
 
		return hashBoard(self.board)
 

	
 
	## Removes stones at coordinates marked with True in self.helper.
 
	def _remove(self):
 
		self._fill(EMPTY)
src/go/engine.py
Show inline comments
 
@@ -7,28 +7,23 @@ def transitionSequence(state1, state2, d
 

	
 

	
 
class SpecGo(core.Go):
 
	def __init__(self,boardSize=19):
 
		super().__init__(boardSize)
 

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

	
 
	def listRelevantMoves(self,diff):
 
		"""There can be 3 different changes in the diff: additions, deletions and replacements.
 
		Additions can be taken as relevant right away.
 
		Deletions and replacements had to be captured, so we add their liberties.
 
		Also any non-missing stones of partially deleted (or replaced) groups had to be replayed, so add them too.
 
		Needs to handle: Take n, return 1. Snapback.
 
		Needs to handle snapback.
 
		There's no end to what could be theoretically relevant, but such sequences are long and we will pretend they won't happen."""
 
		res=(set(),set())
 
		for d in diff:
 
			(r,c,action,color)=d
 
			colorKey=(1-color)<<1 # {-1,1}->{1,0}
 
			colorKey=(1-color)>>1 # {-1,1}->{1,0}
 
			if action!="-" and (r,c) not in res[colorKey]:
 
				res[colorKey].add((r,c))
 
			# this is rather sloppy but correct. the time will show if it is effective enough
 
			if action!="+" and (r,c) not in res[colorKey] and (r,c) not in res[1-colorKey]:
 
				self._helper.clear()
 
				self._helper.floodFill(color if action=="-" else 1-color, r, c)
 
@@ -36,19 +31,19 @@ class SpecGo(core.Go):
 
				for (ri,ci) in self._helper.getContinuousArea():
 
					res[colorKey].add((ri,ci))
 
					res[1-colorKey].add((ri,ci))
 
					if ri>0:
 
						res[colorKey].add((ri-1,ci))
 
						res[1-colorKey].add((ri-1,ci))
 
					if ri<self.boardSize:
 
					if ri+1<self.boardSize:
 
						res[colorKey].add((ri+1,ci))
 
						res[1-colorKey].add((ri+1,ci))
 
					if ci>0:
 
						res[colorKey].add((ri,ci-1))
 
						res[1-colorKey].add((ri,ci-1))
 
					if ci<self.boardSize:
 
					if ci+1<self.boardSize:
 
						res[colorKey].add((ri,ci+1))
 
						res[1-colorKey].add((ri,ci+1))
 
		return res
 

	
 

	
 
class Engine:
 
@@ -68,29 +63,30 @@ class Engine:
 
				if seq:
 
					seq.reverse()
 
					return seq
 

	
 
	def dfs(self,state2,limit):
 
		g=self._g
 
		for (r,c) in self._moveList[(g.toMove-1)>>1]:
 
		for (r,c) in self._moveList[(1-g.toMove)>>1]:
 
			if g.board[r][c]!=EMPTY: continue
 
			neighbours=(
 
				g.board[r-1][c] if r>0 else None,
 
				g.board[r+1][c] if r<g.boardSize else None,
 
				g.board[r+1][c] if r+1<g.boardSize else None,
 
				g.board[r][c-1] if c>0 else None,
 
				g.board[r][c+1] if c<g.boardSize else None
 
				g.board[r][c+1] if c+1<g.boardSize else None
 
			)
 
			g.doMove(g.toMove,r,c)
 
			captured=tuple(
 
				coords for (i,coords) in enumerate(((r-1,c),(r+1,c),(r,c-1),(r,c+1)))
 
				if neighbours[i] is not None and neighbours[i]!=EMPTY and g.board[coords[0]][coords[1]]==EMPTY
 
			)
 
			if g.hash()==state2.hash(): return [(-1*g.toMove,r,c)]
 
			if limit>1:
 
				toMove=-1*g.toMove
 
				seq=self.dfs(state2,limit-1)
 
				if seq:
 
					seq.append((-1*g.toMove,r,c))
 
					seq.append((toMove,r,c))
 
					return seq
 
			g.undoMove(r,c,captured)
 
		return False
 

	
 
eng=Engine()
src/go/helperboard.py
Show inline comments
 
@@ -46,9 +46,9 @@ class HelperBoard:
 
	def clear(self):
 
		for i in range(self._visitedCount):
 
			(r,c)=(self._visited[i*2],self._visited[i*2+1])
 
			self._board[r][c]=EMPTY
 
		self._visitedCount=0
 
		for i in range(self._libCount):
 
			(r,c)=(self._libs[i*2],self._visited[i*2+1])
 
			(r,c)=(self._libs[i*2],self._libs[i*2+1])
 
			self._board[r][c]=EMPTY
 
		self._libCount=0
src/tests/testEngine.py
Show inline comments
 
@@ -17,6 +17,39 @@ class TestTransitions(TestCase):
 
			[0,0,0]
 
		])
 
		g=SpecGo(3)
 
		eng=Engine(g)
 
		eng.load(s1,s2-s1)
 
		self.assertEqual(eng.dfs(s2,1),[(1,1,1)])
 

	
 
	def testCapture(self):
 
		s1=BoardState([
 
			[0,-1,0],
 
			[-1,1,0],
 
			[0,-1,0]
 
		])
 
		s2=BoardState([
 
			[0,-1,0],
 
			[-1,0,-1],
 
			[0,-1,0]
 
		])
 
		g=SpecGo(3)
 
		g.toMove=-1
 
		eng=Engine(g)
 
		eng.load(s1,s2-s1)
 
		self.assertEqual(eng.dfs(s2,1),[(-1,1,2)])
 

	
 
	def testMulti(self):
 
		s1=BoardState([
 
			[0,0,0],
 
			[0,0,0],
 
			[0,0,0]
 
		])
 
		s2=BoardState([
 
			[0,0,0],
 
			[0,1,-1],
 
			[0,0,0]
 
		])
 
		g=SpecGo(3)
 
		eng=Engine(g)
 
		eng.load(s1,s2-s1)
 
		self.assertEqual(eng.dfs(s2,2),[(-1,1,2),(1,1,1)])
src/tests/testGo.py
Show inline comments
 
from unittest import TestCase
 

	
 
from go.core import isLegalPosition
 
from go.core import isLegalPosition, Go
 

	
 

	
 
class TestLegal(TestCase):
 
	def testLegal(self):
 
		board=[
 
			[0,0,0,0,0],
 
@@ -20,6 +20,28 @@ class TestLegal(TestCase):
 
			[1,-1,1,0,0],
 
			[0,1,0,0,0],
 
			[0,0,0,0,0],
 
			[0,0,0,0,0]
 
		]
 
		self.assertFalse(isLegalPosition(board))
 

	
 

	
 
class TestMove(TestCase):
 
	def testCapture(self):
 
		g=Go(3)
 
		g.load([
 
			[0,1,0],
 
			[1,-1,0],
 
			[0,1,0]
 
		])
 
		g.toMove=1
 
		g.doMove(1,1,2)
 
		self.assertEqual(g.board,[
 
			[0,1,0],
 
			[1,0,1],
 
			[0,1,0]
 
		])
 

	
 
		g._helper.clear()
 
		for row in g._helper._board:
 
			for x in row:
 
				self.assertEqual(x,0)
src/tests/testHelper.py
Show inline comments
 
new file 100644
 
from unittest import TestCase
 

	
 
from go.helperboard import HelperBoard
 

	
 

	
 
class TestFlood(TestCase):
 
	def testCapture(self):
 
		b=[
 
			[0,1,0],
 
			[1,-1,1],
 
			[0,1,0]
 
		]
 
		h=HelperBoard(b)
 
		self.assertEqual(h.floodFill(-1,1,1,0), False)
 
		captured=set(h.getContinuousArea())
 
		self.assertEqual(captured, {(1,1)})
0 comments (0 inline, 0 general)