Changeset - 79f823432bb4
[Not reviewed]
default
0 3 0
Laman - 8 years ago 2017-01-07 16:17:22

nicer board logs
3 files changed with 28 insertions and 22 deletions:
0 comments (0 inline, 0 general)
src/go.py
Show inline comments
 
class Go:
 
	EMPTY=0
 
	BLACK=1
 
	WHITE=-1
 
EMPTY=0
 
BLACK=1
 
WHITE=-1
 
 
	## Initializes self.board to a list[r][c]=Go.EMPTY.
 
 
class Go:
 
	## Initializes self.board to a list[r][c]=EMPTY.
 
	def __init__(self,boardSize=19):
 
		self.boardSize=boardSize
 
		self.board=[[Go.EMPTY]*self.boardSize for x in range(boardSize)]
 
		self.board=[[EMPTY]*self.boardSize for x in range(boardSize)]
 
 
	## Executes a move.
 
	#
 
	#  Doesn't check for kos. Suicide not allowed.
 
	#
 
	#  @param color Go.BLACK or Go.WHITE
 
	#  @param color BLACK or WHITE
 
	#  @return True on success, False on failure (illegal move)
 
	def move(self,color,row,col):
 
		if self.board[row][col]!=Go.EMPTY: return False
 
		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.temp=[[False]*self.boardSize for x in self.board]
 
			if not self._floodFill(-color,row+r,col+c): self._remove()
 
 
		# check for suicide
 
		self.temp=[[False]*self.boardSize for x in self.board]
 
		if not self._floodFill(color,row,col):
 
			self.board[row][col]=Go.EMPTY
 
			self.board[row][col]=EMPTY
 
			return False
 
		return True
 
 
	## 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]==Go.EMPTY: return True # found a liberty
 
		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]=Go.EMPTY
 
				if self.temp[r][c]: self.board[r][c]=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)
src/gui/__init__.py
Show inline comments
 
import threading
 
import logging as log
 
import tkinter as tk
 
from PIL import ImageTk
 
import PIL
 
 
import config
 
from epoint import EPoint
 
from corners import Corners
 
import image_analyzer
 
from go import Go
 
import go
 
from grid import Grid
 
 
 
class MainWindow(tk.Frame):
 
	def __init__(self,parent,master=None):
 
		self.parent=parent
 
		self.corners=Corners()
 
 
		self.currentFrame=None
 
		self._boardGrid=None
 
 
		self.img=None
 
@@ -142,28 +142,28 @@ class BoardView(tk.Canvas):
 
 
	def drawStars(self):
 
		for r in range(4,19,6):
 
			for c in range(4,19,6):
 
				self.create_oval(r*18-2,c*18-2,r*18+2,c*18+2,fill='#000000')
 
 
	## Draws a stone at provided coordinates.
 
	#
 
	#  For an unknown color draws nothing and returns False.
 
	#
 
	#  @param r row coordinate, [0-18], counted from top
 
	#  @param c column coordinate, [0-18], counted from left
 
	#  @param color color indicator, Go.BLACK or Go.WHITE
 
	#  @param color color indicator, go.BLACK or go.WHITE
 
	def drawStone(self,r,c,color):
 
		if color==Go.BLACK: hexCode='#000000'
 
		elif color==Go.WHITE: hexCode='#ffffff'
 
		if color==go.BLACK: hexCode='#000000'
 
		elif color==go.WHITE: hexCode='#ffffff'
 
		else: return False
 
		r+=1
 
		c+=1
 
		self.create_oval(c*18-9,r*18-9,c*18+9,r*18+9,fill=hexCode)
 
 
 
class GUI:
 
	def __init__(self):
 
		self.root = tk.Tk()
 
		self.root.title("OneEye {0}.{1}.{2}".format(*config.misc.version))
 
 
		self._coreMessages=None
src/image_analyzer.py
Show inline comments
 
import logging as log
 
from grid import Grid
 
from go import Go
 
from go import exportBoard
 
import go
 
 
 
class ImageAnalyzer:
 
 
	def __init__(self,tresB=30,tresW=60):
 
		self.board=[[Go.EMPTY]*19 for r in range(19)]
 
		self.board=[[go.EMPTY]*19 for r in range(19)]
 
		self.grid=None
 
 
		self.tresB=tresB
 
		self.tresW=tresW
 
 
	# let's not concern ourselves with sizecoef and shift here anymore. we want corners to come already properly recomputed
 
	def analyze(self,image):
 
		if self.grid==None:
 
			log.info("ImageAnalyzer.analyze() aborted: no grid available.")
 
			return False
 
 
		for r in range(19):
 
			for c in range(19):
 
				intersection=self.grid.intersections[r][c]
 
 
				self.board[r][c]=self.analyzePoint(image,r,c,intersection,*(self.grid.stoneSizeAt(r,c)))
 
 
		boardStr="\n".join(str(row) for row in self.board)
 
		log.info("board analyzed:\n%s",boardStr)
 
		log.info("board analyzed:\n%s", exportBoard(self.board))
 
 
	def analyzePoint(self,image,row,col,imageCoords,stoneWidth,stoneHeight):
 
		b=w=e=0
 
 
		((x1,y1),(x2,y2))=relevantRect(imageCoords,stoneWidth,stoneHeight)
 
 
		for y in range(y1,y2+1):
 
			for x in range(x1,x2+1):
 
				red,green,blue=image.getpixel((x,y))
 
 
				I=(red+green+blue)/255/3
 
				m=min(red,green,blue)
 
				S=1-m/I
 
				if 100*I<self.tresB: b+=1
 
				elif 100*I>self.tresW: w+=1
 
				else: e+=1
 
 
		log.debug("(%d,%d) ... (b=%d,w=%d,e=%d)", row, col, b, w, e)
 
 
		if b>=w and b>=e: return Go.BLACK
 
		if w>=b and w>=e: return Go.WHITE
 
		return Go.EMPTY
 
		if b>=w and b>=e: return go.BLACK
 
		if w>=b and w>=e: return go.WHITE
 
		return go.EMPTY
 
 
	def setGridCorners(self,corners):
 
		self.grid=Grid(corners)
 
 
 
def relevantRect(imageCoords,stoneWidth,stoneHeight):
 
	x=int(imageCoords.x)
 
	y=int(imageCoords.y)
 
	xmax=max(int(stoneWidth*2//7), 2) # !! optimal parameters subject to further research
 
	ymax=max(int(stoneHeight*2//7), 2)
 
 
	return ((x-xmax,y-ymax), (x+xmax,y+ymax))
0 comments (0 inline, 0 general)