Changeset - f669c32706e5
[Not reviewed]
default
0 3 2
Laman - 7 years ago 2018-04-13 18:15:48

refactoring: much of SourceFile.createDiagram moved to drawer, created drawer.Base class
5 files changed with 123 insertions and 101 deletions:
0 comments (0 inline, 0 general)
src/__init__.py
Show inline comments
 
new file 100644
src/diana.py
Show inline comments
 
import os
 
import re
 

	
 
from jinja2 import Environment,FileSystemLoader
 

	
 
import config as cfg
 
import go
 
from go import BLACK,WHITE,EMPTY
 
from sgfParser import ParserError
 
from sgfParser.collection import Collection
 
from drawer.svg import Svg
 
from drawer.tikz import Tikz
 

	
 

	
 
curDir=os.path.dirname(__file__)
 
templateDir=os.path.join(curDir,"templ")
 
env=Environment(loader=FileSystemLoader(templateDir))
 
env.trim_blocks=True
 
env.lstrip_blocks=True
 

	
 

	
 
def collectMoves(root):
 
	node=root
 
	while len(node.children)>0:
 
		b=node.getProp("B")
 
		w=node.getProp("W")
 
		if b is not None: yield ("b",b)
 
@@ -33,116 +24,99 @@ def collectMoves(root):
 

	
 
class SourceFile:
 
	def __init__(self,fileName):
 
		self.fileName=fileName
 
		self._shortName= "".join(re.split(r'[/\\]', fileName)[-1].split('.')[:-1])
 
		self._game=go.Go()
 
		self._moveNumber=0
 
		self._record=None
 
		self._moves=[]
 

	
 
		games=Collection(open(self.fileName, 'r', encoding=cfg.encoding).read()).listGames()
 
		self._record=list(games)[0]
 
		self._moves=list(collectMoves(self._record.root))
 

	
 
	def process(self):
 
		print("{0}... ".format(self.fileName), end="")
 

	
 
		try:
 
			games=Collection(open(self.fileName, 'r', encoding=cfg.encoding).read()).listGames()
 
		except ParserError as e:
 
			print("Couldn't parse {0}, following error occured: {1}".format(self.fileName,e))
 
			return False
 
		self._record=list(games)[0]
 
		self._moves=list(collectMoves(self._record.root))
 

	
 
		diagramsNeeded=(len(self._moves)-cfg.minMovesPerDiagram)//cfg.movesPerDiagram+1
 

	
 
		for i in range(diagramsNeeded):
 
			self.createDiagram(i+1)
 
		i=1
 
		for k in range(0,len(self._moves),cfg.movesPerDiagram):
 
			filename=os.path.join(cfg.outputDir,"{0}-{1}".format(self._shortName,i))
 
			self.createDiagram(k,k+cfg.movesPerDiagram).save(filename,"templ-pleb.svg")
 
			i+=1
 

	
 
		notes=open(os.path.join(cfg.outputDir,"{0}.txt".format(self._shortName)), 'w')
 
		notes.write(self.createGameInfo())
 
		notes.close()
 
		print("done")
 

	
 
	def createDiagram(self,diagramNumber):
 
	def createDiagram(self,start,end):
 
		# initialize the diagram
 
		template=Svg()
 
		overlays=[]
 
		letters=dict()
 
		letter="a"
 

	
 
		self._setMove(start)
 

	
 
		# draw current state
 
		for lineNumber,line in enumerate(self._game.board):
 
			for itemNumber,item in enumerate(line):
 
				if item==BLACK: template.addStone(itemNumber,lineNumber,"b")
 
				if item==WHITE: template.addStone(itemNumber,lineNumber,"w")
 
			localBoard={(a,b):self._game.board[b][a]-1 for a in range(19) for b in range(19) if self._game.board[b][a]!=EMPTY}
 
				if item!=EMPTY: template.addStone(itemNumber,lineNumber,"b" if item==BLACK else "w")
 

	
 
		for j in range(cfg.movesPerDiagram):
 
			# draw the moves
 
			if self._moveNumber>=len(self._moves): break
 
		for k in range(start,end):
 
			if k>=len(self._moves): break
 

	
 
			c,move=self._moves[self._moveNumber]
 
			c=c.lower()
 
			color,move=self._moves[k]
 
			if move==tuple():
 
				overlays.append("{0} pass".format(self._moveNumber))
 
				self._moveNumber+=1
 
				template.overlays.append((k,"pass")) # !!
 
				continue
 
			else:
 
				(x,y)=move
 

	
 
			if not self._game.move(BLACK if c=='b' else WHITE, x,y):
 
				# !! we do not honor http://red-bean.com/sgf/ff5/m_vs_ax.htm at the moment
 
				msg="illegal move: {0} at {1},{2}".format(self._moveNumber+1,x,y)
 
				if cfg.keepBroken:
 
					print(msg)
 
					self._moveNumber+=1
 
					continue
 
				else:
 
					msg+=". aborted"
 
					print(msg)
 
					return False
 
				(c,r)=move
 

	
 
			# draw the move on an empty intersection
 
			if not (x,y) in localBoard:
 
				localBoard[(x,y)]=self._moveNumber+1
 
				template.addMove(x,y,c,self._moveNumber+1)
 
			# intersection occupied by an unlabeled stone
 
			elif localBoard[(x,y)]<1:
 
				# intersection not labeled even by a letter
 
				if not (x,y) in letters:
 
					letters[(x,y)]=letter
 
					color='b' if localBoard[(x,y)]==EMPTY else 'w'
 
					template.addMove(x,y,color,letter)
 
					letter=chr(ord(letter)+1)
 
				overlays.append("{0} = {1}".format(self._moveNumber+1,letters[(x,y)]))
 
			# intersection occupied by a numbered stone
 
			else: overlays.append("{0} = {1}".format(self._moveNumber+1,localBoard[(x,y)]))
 
			if not self._move(color,c,r):
 
				if cfg.keepBroken: continue
 
				else: return False
 

	
 
			self._moveNumber+=1
 
			# draw the move
 
			template.addMove(c,r,color,k+1)
 
		
 
		# finish and save the diagram
 
		file=open(os.path.join(cfg.outputDir,"{0}-{1}.{2}".format(self._shortName,diagramNumber,template.extension)),'w') # a new file
 
		file.write(template.render(env.get_template("templ.svg")))
 
		file.close()
 

	
 
		notes=open(os.path.join(cfg.outputDir,"{0}-{1}.txt".format(self._shortName,diagramNumber)), 'w')
 
		notes.write("\n".join(overlays))
 
		notes.close()
 
		return template
 

	
 
	def createGameInfo(self):
 
		rec=self._record
 
		return """{title}
 
B: {black} {bRank}
 
W: {white} {wRank}
 
{date}
 
{result}""".format(title=rec.get("GN",""), black=rec.get("PB",""), bRank=rec.get("BR",""), white=rec.get("PW",""), wRank=rec.get("WR",""), date=rec.get("DT",""), result=rec.get("RE",""))
 

	
 
	def _setMove(self,k):
 
		self._game=go.Go()
 
		for i in range(k):
 
			(color,move)=self._moves[i]
 
			if move==tuple(): continue # pass
 
			self._move(color,*move)
 

	
 
	def _move(self,color,c,r):
 
		if not self._game.move(BLACK if color=='b' else WHITE, c,r):
 
			# !! we do not honor http://red-bean.com/sgf/ff5/m_vs_ax.htm at the moment
 
			msg="illegal move: {0} at {1},{2}".format(self._game.moveCount+1,c,r)
 
			if cfg.keepBroken:
 
				print(msg)
 
			else:
 
				msg+=". aborted"
 
				print(msg)
 
				return False
 
		return True
 

	
 

	
 
if __name__=="__main__":
 
print("processing:")
 
files=cfg.inputFiles[:]
 

	
 
for item in files:
 
	if os.path.isfile(item):
 
			try:
 
		f=SourceFile(item)
 
		f.process()
 
			except ParserError as e:
 
				print("Couldn't parse {0}, following error occured: {1}".format(item,e))
 
	elif os.path.isdir(item):
 
		files+=[os.path.join(item,child) for child in os.listdir(item)]
 
		print("contents of the '{0}' directory added to the queue".format(item))
 
	else: print("the '{0}' path could not be resolved to either a file nor a directory".format(item))
src/drawer/base.py
Show inline comments
 
new file 100644
 
import os
 
from itertools import count
 

	
 
from jinja2 import Environment,FileSystemLoader
 

	
 

	
 
class DiagramPoint:
 
	def __init__(self,x,y,color="",label=""):
 
		self.x=x
 
		self.y=y
 
		self.color=color
 
		self.label=label
 

	
 
	def __repr__(self):
 
		return 'DiagramPoint({0},{1},"{2}","{3}")'.format(self.x,self.y,self.color,self.label)
 

	
 

	
 
class Base:
 
	highNumbers=True
 

	
 
	def __init__(self,start=0):
 
		self.overlays=[]
 
		self._letter="a"
 

	
 
		self._index=dict()
 
		self._indexGen=count(start)
 

	
 
		curDir=os.path.dirname(__file__)
 
		templateDir=os.path.join(curDir,"..","templ")
 
		self._env=Environment(loader=FileSystemLoader(templateDir))
 
		self._env.trim_blocks=True
 
		self._env.lstrip_blocks=True
 

	
 
	def addStone(self,x,y,color):
 
		assert (x,y) not in self._index
 
		self._index[(x,y)]=(next(self._indexGen),DiagramPoint(x,y,color))
 

	
 
	def addMove(self,x,y,color,label):
 
		if (not self.highNumbers) and isinstance(label,int) and label%100!=0:
 
			label%=100
 

	
 
		if (x,y) not in self._index:
 
			self._index[(x,y)]=(next(self._indexGen),DiagramPoint(x,y,color,label))
 
		else:
 
			(_,point)=self._index[(x,y)]
 
			if not point.label:
 
				point.label=self._letter
 
				self._letter=chr(ord(self._letter)+1)
 
			self.overlays.append((label, point.label))
 

	
 
	def addLabel(self,x,y,label):
 
		self._index[(x,y)]=(next(self._indexGen),DiagramPoint(x,y,"",label))
 

	
 
	def save(self,filename):
 
		notes=open(filename+".txt", 'w')
 
		notes.write("\n".join("{0} = {1}".format(a,b) for (a,b) in self.overlays))
 
		notes.close()
src/drawer/svg.py
Show inline comments
 
from itertools import count
 

	
 

	
 
c=28
 
padding=15
 
highNumbers=True
 
from .base import Base
 

	
 

	
 
def adjustFont(base,text):
 
	text=str(text)
 
	if len(text)<2: return round(0.7*base)
 
	elif len(text)<3: return round(0.55*base)
 
@@ -21,41 +16,34 @@ class DiagramPoint:
 
		self.label=label
 

	
 
	def __repr__(self):
 
		return 'DiagramPoint({0},{1},"{2}","{3}")'.format(self.x,self.y,self.color,self.label)
 

	
 

	
 
class Svg:
 
class Svg(Base):
 
	extension="svg"
 

	
 
	padding=15
 
	highNumbers=True
 
	
 
	def __init__(self):
 
	def __init__(self,start=0):
 
		super().__init__(start)
 
		self.boardSize=480
 
		self.padding=30
 

	
 
		self._index=dict()
 
		self._indexGen=count()
 

	
 
	def addStone(self,x,y,color):
 
		self._index[(x,y)]=(next(self._indexGen),DiagramPoint(x,y,color))
 

	
 
	def addMove(self,x,y,color,label):
 
		if (not self.highNumbers) and isinstance(label,int) and label%100!=0:
 
			label%=100
 

	
 
		self._index[(x,y)]=(next(self._indexGen),DiagramPoint(x,y,color,label))
 

	
 
	def addLabel(self,x,y,label):
 
		self._index[(x,y)]=(next(self._indexGen),DiagramPoint(x,y,"",label))
 

	
 
	def render(self, template):
 
	def render(self, templateName):
 
		points = [p for (i,p) in sorted(self._index.values(), key=lambda x: x[0])]
 

	
 
		stones = [p for p in points if p.color and not p.label]
 
		stones = [p for p in points if p.color and p.label==""]
 
		moves = [p for p in points if p.color and p.label]
 
		labels = [p for p in points if not p.color and p.label]
 

	
 
		params = {"boardSize":self.boardSize, "padding":self.padding, "stones":stones, "moves":moves, "labels":labels, "adjustFont":adjustFont}
 

	
 
		return template.render(params)
 
		return self._env.get_template(templateName).render(params)
 

	
 
	def save(self,filename,template="templ.svg"):
 
		file=open(filename+".svg",'w')
 
		file.write(self.render(template))
 
		file.close()
 

	
 
		super().save(filename)
src/go.py
Show inline comments
 
@@ -3,13 +3,15 @@ WHITE=-1
 
EMPTY=0
 

	
 

	
 
class Go:
 
	board=[[EMPTY]*19 for i in range(19)]
 
	
 
	def __init__(self): self.board=[[EMPTY]*19 for i in range(19)]
 
	def __init__(self):
 
		self.board=[[EMPTY]*19 for i in range(19)]
 
		self.moveCount=0
 
	
 
	def move(self,color,y,x):
 
		if self.board[x][y]!=EMPTY: return False
 

	
 
		self.board[x][y]=color
 

	
 
@@ -17,12 +19,13 @@ class Go:
 
			self.temp=[[False]*19 for i in range(19)]
 
			if not self._floodFill(-color,x+i,y+j): self._remove()
 
		self.temp=[[False]*19 for i in range(19)]
 
		if not self._floodFill(color,x,y):
 
			self.board[x][y]=EMPTY
 
			return False
 
		self.moveCount+=1
 
		return True
 

	
 
	def _floodFill(self,color,x,y):
 
		if x<0 or x>18 or y<0 or y>18: return False
 
		if self.temp[x][y]: return False
 
		if self.board[x][y]==EMPTY: return True
0 comments (0 inline, 0 general)