diff --git a/src/diana.py b/src/diana.py --- a/src/diana.py +++ b/src/diana.py @@ -1,120 +1,120 @@ -import os -import re -import sys - -import go -from go import BLACK,WHITE,EMPTY -from sgfParser.collection import Collection - -from drawer.svg import Svg -from drawer.tikz import Tikz - - -templateDir=os.path.join(os.path.dirname(__file__),"templ") -with open(os.path.join(templateDir,"templ.svg")) as f: - template=f.read() - - -if len(sys.argv)>1: - files=sys.argv[1:] -else: - sys.exit("no input file specified") - -movesPerDiagram=75 -minMovesPerDiagram=10 - -t=sys.stdout - - -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) - elif w is not None: yield ("w",w) - # else: yield None # !! not really robust - - node=node.children[0] - - -def processFile(fileName): - shortName="".join(re.split(r'[/\\]',fileName)[-1].split('.')[:-1]) - - game=go.Go() - global t - - games=Collection(open(fileName, 'r', encoding="utf-8").read()).listGames() - record=list(games)[0] - - moves=list(collectMoves(record)) - - localBoard=dict() - overlays="" - letters=dict() - letter='a' - - diagramsNeeded=(len(moves)-minMovesPerDiagram)//movesPerDiagram+1 - moveNumber=0 - - for i in range(diagramsNeeded): - # initialize the diagram - diagram=Svg() - - for lineNumber,line in enumerate(game.board): - for itemNumber,item in enumerate(line): - if item==BLACK: diagram.addStone(itemNumber,lineNumber,"b") - if item==WHITE: diagram.addStone(itemNumber,lineNumber,"w") - localBoard={(a,b):game.board[b][a]-1 for a in range(19) for b in range(19) if game.board[b][a]!=EMPTY} - - for j in range(movesPerDiagram): - # draw the moves - if moveNumber>=len(moves): break - - c,(x,y)=moves[moveNumber] - c=c.lower() - - if not game.move(BLACK if c=='b' else WHITE, x,y): - print("illegal move: {0} at {1},{2}".format(moveNumber+1,x,y)) - moveNumber+=1 - continue - - # draw the move on an empty intersection - if not (x,y) in localBoard: - localBoard[(x,y)]=moveNumber+1 - diagram.addMove(x,y,c,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' - diagram.addMove(x,y,color,letter) - letter=chr(ord(letter)+1) - overlays+="{0} = {1}\n".format(moveNumber+1,letters[(x,y)]) - # intersection occupied by a numbered stone - else: overlays+="{0} = {1}\n".format(moveNumber+1,localBoard[(x,y)]) - - moveNumber+=1 - - # finish and save the diagram - t=open(os.path.join("out","{0}-{1}.{2}".format(shortName,i+1,diagram.extension)),'w') # a new file - t.write(diagram.render(template)) - t.close() - - notes=open(os.path.join("out","{0}.txt".format(shortName)),'w') - notes.write(overlays) - notes.close() - -print("processing:") -for item in files: - # relativně vůči work directory nebo vůči skriptu? - # item=os.path.join(os.path.dirname(__file__),item) - if os.path.isfile(item): - print("{0}... ".format(item),end="") - processFile(item) - print("done") - 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)) +import os +import re +import sys + +import go +from go import BLACK,WHITE,EMPTY +from sgfParser.collection import Collection + +from drawer.svg import Svg +from drawer.tikz import Tikz + + +templateDir=os.path.join(os.path.dirname(__file__),"templ") +with open(os.path.join(templateDir,"templ.svg")) as f: + template=f.read() + + +if len(sys.argv)>1: + files=sys.argv[1:] +else: + sys.exit("no input file specified") + +movesPerDiagram=75 +minMovesPerDiagram=10 + +t=sys.stdout + + +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) + elif w is not None: yield ("w",w) + # else: yield None # !! not really robust + + node=node.children[0] + + +def processFile(fileName): + shortName="".join(re.split(r'[/\\]',fileName)[-1].split('.')[:-1]) + + game=go.Go() + global t + + games=Collection(open(fileName, 'r', encoding="utf-8").read()).listGames() + record=list(games)[0] + + moves=list(collectMoves(record)) + + localBoard=dict() + overlays="" + letters=dict() + letter='a' + + diagramsNeeded=(len(moves)-minMovesPerDiagram)//movesPerDiagram+1 + moveNumber=0 + + for i in range(diagramsNeeded): + # initialize the diagram + diagram=Svg() + + for lineNumber,line in enumerate(game.board): + for itemNumber,item in enumerate(line): + if item==BLACK: diagram.addStone(itemNumber,lineNumber,"b") + if item==WHITE: diagram.addStone(itemNumber,lineNumber,"w") + localBoard={(a,b):game.board[b][a]-1 for a in range(19) for b in range(19) if game.board[b][a]!=EMPTY} + + for j in range(movesPerDiagram): + # draw the moves + if moveNumber>=len(moves): break + + c,(x,y)=moves[moveNumber] + c=c.lower() + + if not game.move(BLACK if c=='b' else WHITE, x,y): + print("illegal move: {0} at {1},{2}".format(moveNumber+1,x,y)) + moveNumber+=1 + continue + + # draw the move on an empty intersection + if not (x,y) in localBoard: + localBoard[(x,y)]=moveNumber+1 + diagram.addMove(x,y,c,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' + diagram.addMove(x,y,color,letter) + letter=chr(ord(letter)+1) + overlays+="{0} = {1}\n".format(moveNumber+1,letters[(x,y)]) + # intersection occupied by a numbered stone + else: overlays+="{0} = {1}\n".format(moveNumber+1,localBoard[(x,y)]) + + moveNumber+=1 + + # finish and save the diagram + t=open(os.path.join("out","{0}-{1}.{2}".format(shortName,i+1,diagram.extension)),'w') # a new file + t.write(diagram.render(template)) + t.close() + + notes=open(os.path.join("out","{0}.txt".format(shortName)),'w') + notes.write(overlays) + notes.close() + +print("processing:") +for item in files: + # relativně vůči work directory nebo vůči skriptu? + # item=os.path.join(os.path.dirname(__file__),item) + if os.path.isfile(item): + print("{0}... ".format(item),end="") + processFile(item) + print("done") + 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/go.py b/src/go.py --- a/src/go.py +++ b/src/go.py @@ -1,36 +1,36 @@ -BLACK=1 -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 move(self,color,y,x): - if self.board[x][y]!=EMPTY: return False - - self.board[x][y]=color - - for i,j in ((-1,0),(1,0),(0,-1),(0,1)): - 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 - 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 - if self.board[x][y]!=color: return False - self.temp[x][y]=True - return self._floodFill(color,x-1,y) or self._floodFill(color,x+1,y) or self._floodFill(color,x,y-1) or self._floodFill(color,x,y+1) - - def _remove(self): - for i in range(19): - for j in range(19): - if self.temp[i][j]: self.board[i][j]=EMPTY +BLACK=1 +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 move(self,color,y,x): + if self.board[x][y]!=EMPTY: return False + + self.board[x][y]=color + + for i,j in ((-1,0),(1,0),(0,-1),(0,1)): + 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 + 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 + if self.board[x][y]!=color: return False + self.temp[x][y]=True + return self._floodFill(color,x-1,y) or self._floodFill(color,x+1,y) or self._floodFill(color,x,y-1) or self._floodFill(color,x,y+1) + + def _remove(self): + for i in range(19): + for j in range(19): + if self.temp[i][j]: self.board[i][j]=EMPTY diff --git a/src/sgfParser/collection.py b/src/sgfParser/collection.py --- a/src/sgfParser/collection.py +++ b/src/sgfParser/collection.py @@ -1,93 +1,93 @@ -from sgfParser.node import Node -from . import skipWhitespace, ParserError - - -class Collection: - def __init__(self,s): - self.gameTrees=[] - i=skipWhitespace(s,0) - if i>=len(s): return - elif not GameTree.fits(s,i): - raise ParserError("expected a GameTree starting with '('",s,i) - while GameTree.fits(s,i): - i,x=GameTree.create(s,i) - self.gameTrees.append(x) - if i=len(s) or s[i]!=")": - raise ParserError("expected end of a GameTree marked by ')'",s,i) - i=skipWhitespace(s,i+1) - return (i,res) - - ## Expand multiple games into distinct GameTrees and yield each. - def listGames(self): - for node in self._listGINodes(): - yield self._buildSubtree(node) - - def getNode(self,i): - if 0<=i=len(s): return + elif not GameTree.fits(s,i): + raise ParserError("expected a GameTree starting with '('",s,i) + while GameTree.fits(s,i): + i,x=GameTree.create(s,i) + self.gameTrees.append(x) + if i=len(s) or s[i]!=")": + raise ParserError("expected end of a GameTree marked by ')'",s,i) + i=skipWhitespace(s,i+1) + return (i,res) + + ## Expand multiple games into distinct GameTrees and yield each. + def listGames(self): + for node in self._listGINodes(): + yield self._buildSubtree(node) + + def getNode(self,i): + if 0<=i