Files @ 7cb01d4080c9
Branch filter:

Location: OneEye/src/statebag/boardstate.py - annotation

Laman
a hinted neural network (failed)
e4c93735285e
eb6c3126cf26
eb6c3126cf26
eb6c3126cf26
eb6c3126cf26
e4c93735285e
e4c93735285e
e4c93735285e
eb6c3126cf26
e4c93735285e
eb6c3126cf26
eb6c3126cf26
eb6c3126cf26
eb6c3126cf26
eb6c3126cf26
eb6c3126cf26
eb6c3126cf26
eb6c3126cf26
eb6c3126cf26
eb6c3126cf26
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
9ab11204b0f9
9ab11204b0f9
9ab11204b0f9
9ab11204b0f9
9ab11204b0f9
9ab11204b0f9
9ab11204b0f9
9ab11204b0f9
9ab11204b0f9
9ab11204b0f9
9ab11204b0f9
9ab11204b0f9
9ab11204b0f9
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
eb6c3126cf26
eb6c3126cf26
e4c93735285e
eb6c3126cf26
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
9ab11204b0f9
9ab11204b0f9
9ab11204b0f9
9ab11204b0f9
9ab11204b0f9
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
5b4c6f5a0a28
5b4c6f5a0a28
e4c93735285e
e4c93735285e
5b4c6f5a0a28
5b4c6f5a0a28
5b4c6f5a0a28
5b4c6f5a0a28
5b4c6f5a0a28
5b4c6f5a0a28
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
e4c93735285e
from util import EMPTY,BLACK,WHITE, hashBoard,exportBoard
from go.engine import getTransitionSequence,SpecGo


g=SpecGo()


## Crude lower bound on edit distance between states.
def estimateDistance(diff,s1,s2):
	# lot of room for improvements
	additions=deletions=replacements=unaccounted=0
	for (r,c,d,color) in diff:
		if d=="+": additions+=1
		elif d=="-":
			deletions+=1
			for (ri,ci) in g.listNeighbours(r,c):
				if s1[ri][ci]==EMPTY and s2[ri][ci]==EMPTY:
					unaccounted+=1
		else: replacements+=1
	if additions>0 or unaccounted>0: return additions+replacements+unaccounted
	elif replacements==0 and deletions>0: return 2 # take n, return 1
	return replacements+1 # ???


class GameTreeNode:
	def __init__(self,parent,color):
		self.parent=parent
		self.color=color # color closing the move sequence
		self.prev=None
		self.moves=[] # move sequence from prev to self
		self.weight=0

	## Connect itself after v if it gains itself more weight.
	def tryConnect(self,v,diff):
		moves=getTransitionSequence(v.parent,self.parent,-1*v.color,self.color,diff)
		if not moves: return
		w=v.weight+2-len(moves) # proper one move transition increases the weight by 1
		if w>self.weight:
			self.moves=moves
			self.prev=v
			self.weight=w

	def exportRecord(self):
		""":return: [(c,row,col), ...]. c in {BLACK,WHITE} == {1,-1}"""
		sequence=[]
		v=self
		while v is not None:
			sequence.append(v)
			v=v.prev

		res=[]
		for v in reversed(sequence):
			res.extend(v.moves)
		return res


class BoardState:
	def __init__(self,board):
		self._board=tuple(tuple(x for x in row) for row in board)
		self.nodes=(GameTreeNode(self,BLACK),GameTreeNode(self,WHITE))
		self.cachedDiff=[]
		self._hash=None

	def tryConnect(self,s,diff=None):
		""":param s: BoardState s
		:param diff: [(r,c,change,color), ...], change in {+,-,*}, color in {BLACK,WHITE}"""
		if diff is None: diff=self-s
		distEst=estimateDistance(diff,s,self)
		if distEst>3: return # we couldn't find every such move sequence anyway without a clever algorithm
		weightEst=s.getWeight()+2-distEst
		if weightEst<=self.getWeight(): return
		for v1 in s.nodes:
			for v2 in self.nodes:
				v2.tryConnect(v1,diff)

	def hash(self):
		if self._hash is None: self._hash=hashBoard(self._board)
		return self._hash

	def export(self):
		return exportBoard(self._board)

	def exportDiff(self,s2):
		return "vvv\n{0}\n=== {1} ===\n{2}\n^^^".format(self.export(), s2-self, s2.export())

	def exportRecord(self):
		""":return: [(c,row,col), ...]. c in {BLACK,WHITE} == {1,-1}"""
		v=self.nodes[0] if self.nodes[0].weight>self.nodes[1].weight else self.nodes[1]
		return v.exportRecord()

	def __iter__(self): return iter(self._board)

	def __getitem__(self,key): return self._board[key]

	## Compute difference self-s.
	def __sub__(self,s):
		res=[]

		for (r,(row,rowS)) in enumerate(zip(self._board,s)):
			for (c,(item,itemS)) in enumerate(zip(row,rowS)):
				if item==itemS: continue
				elif itemS==EMPTY: res.append((r,c,"+",item))
				elif item==EMPTY: res.append((r,c,"-",itemS))
				else: res.append((r,c,"*",item)) # ->to
		return res

	def __eq__(self,x):
		return self.hash()==x.hash()

	def setWeight(self,val):
		for v in self.nodes: v.weight=val

	def getWeight(self):
		return max(v.weight for v in self.nodes)

	def getPrev(self):
		v=max(self.nodes, key=lambda v: v.weight)
		return v.prev.parent if v.prev else None