diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 diff --git a/src/diana.py b/src/diana.py --- a/src/diana.py +++ b/src/diana.py @@ -1,8 +1,6 @@ import os import re -from jinja2 import Environment,FileSystemLoader - import config as cfg import go from go import BLACK,WHITE,EMPTY @@ -12,13 +10,6 @@ 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: @@ -36,95 +27,55 @@ class SourceFile: 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 + # draw the moves + 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 - - # 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() + # draw the move + template.addMove(c,r,color,k+1) - 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 @@ -134,15 +85,38 @@ 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","")) - -print("processing:") -files=cfg.inputFiles[:] + 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) -for item in files: - if os.path.isfile(item): - f=SourceFile(item) - f.process() - 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)) + 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)) diff --git a/src/drawer/base.py b/src/drawer/base.py new file mode 100644 --- /dev/null +++ b/src/drawer/base.py @@ -0,0 +1,57 @@ +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() diff --git a/src/drawer/svg.py b/src/drawer/svg.py --- a/src/drawer/svg.py +++ b/src/drawer/svg.py @@ -1,9 +1,4 @@ -from itertools import count - - -c=28 -padding=15 -highNumbers=True +from .base import Base def adjustFont(base,text): @@ -24,38 +19,31 @@ class DiagramPoint: 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) diff --git a/src/go.py b/src/go.py --- a/src/go.py +++ b/src/go.py @@ -6,7 +6,9 @@ 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 @@ -20,6 +22,7 @@ class Go: 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):