Changeset - c95fa8ab1067
[Not reviewed]
default
1 4 2
Laman - 3 years ago 2022-03-04 22:55:14

changed imports
6 files changed with 20 insertions and 14 deletions:
0 comments (0 inline, 0 general)
.hgignore
Show inline comments
 
^\.
 
__pycache__/
 
^in/
 
^out/
 
\.pyc$
 
^build/
 
^dist/
 
.egg-info/
src/diana/__main__.py
Show inline comments
 
new file 100644
 
from .diana import main
 

	
 
if __name__ == "__main__":
 
	main()
src/diana/diana.py
Show inline comments
 
import os
 
import re
 

	
 
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
 
from . import config as cfg
 
from . 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
 

	
 

	
 
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]
 

	
 

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

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

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

	
 
		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
 

	
 
		infoStr="""{GN}
 
B: {PB} {BR}
 
W: {PW} {WR}
 
{DT}
 
{RE}""".format(**self.fetchGameInfo(["GN","PB","BR","PW","WR","DT","RE"],""))
 
		notes=open(os.path.join(cfg.outputDir,"{0}.txt".format(self._shortName)), 'w')
 
		notes.write(infoStr)
 
		notes.close()
 
		print("done")
 

	
 
	def createDiagram(self,start,end):
 
		# initialize the diagram
 
		template=Svg()
 

	
 
		self._setMove(start)
 

	
 
		# draw current state
 
		for lineNumber,line in enumerate(self._game.board):
 
			for itemNumber,item in enumerate(line):
 
				if item!=EMPTY: template.addStone(itemNumber,lineNumber,"b" if item==BLACK else "w")
 

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

	
 
			color,move=self._moves[k]
 
			if move==tuple():
 
				template.overlays.append((k,"pass")) # !!
 
				continue
 
			else:
 
				(c,r)=move
 

	
 
			if not self._move(color,c,r):
 
				if cfg.keepBroken: continue
 
				else: return False
 

	
 
			# draw the move
 
			template.addMove(c,r,color,k+1)
 

	
 
		return template
 

	
 
	def fetchGameInfo(self,fieldNames,default=None):
 
		return {k:self._record.get(k,default) for k in fieldNames}
 

	
 
	def _setMove(self,k):
 
		self._game=go.Go()
 

	
 
		blackStones=self._record.root.getProp("AB")
 
		whiteStones=self._record.root.getProp("AW")
 
		if blackStones:
 
			for p in blackStones:
 
				self._game.board[p.r][p.c]=BLACK
 
		if whiteStones:
 
			for p in whiteStones:
 
				self._game.board[p.r][p.c]=WHITE
 

	
 
		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__":
 
def main():
 
	cfg.parseArgs()
 
	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/diana/sgfParser/collection.py
Show inline comments
 
from .node import Node
 
from . import skipWhitespace, ParserError
 
from gameRecord import GameRecord
 
from .gameRecord import GameRecord
 

	
 

	
 
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):
 
			raise ParserError("expected EOF",s,i)
 

	
 
	def listGames(self):
 
		for tree in self.gameTrees:
 
			for game in tree.listGames(): yield game
 

	
 

	
 
class GameTree:
 
	def __init__(self):
 
		self.nodes=[]
 
		self.branches=[]
 

	
 
	@staticmethod
 
	def fits(s,i):
 
		return i<len(s) and s[i]=="("
 

	
 
	@staticmethod
 
	def create(s,start):
 
		assert GameTree.fits(s,start)
 
		res=GameTree()
 

	
 
		i=skipWhitespace(s,start+1)
 
		if not Node.fits(s,i):
 
			raise ParserError("expected a Node starting with ';'",s,i)
 

	
 
		y=None
 
		while Node.fits(s,i):
 
			i,x=Node.create(s,i)
 
			res.nodes.append(x)
 
			if y: y.addChild(x)
 
			x.parent=y
 
			y=x
 
			i=skipWhitespace(s,i)
 

	
 
		while GameTree.fits(s,i):
 
			i,x=GameTree.create(s,i)
 
			res.branches.append(x)
 
			subroot=x.getNode(0)
 
			subroot.parent=y
 
			if y: y.addChild(subroot)
 
			i=skipWhitespace(s,i)
 
		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):
 
		if len(self.nodes)==0: return None
 
		for node in self.nodes[0].listGINodes():
 
			yield GameRecord(self._buildSubtree(node))
 

	
 
	def getNode(self,i):
 
		if 0<=i<len(self.nodes):
 
			return self.nodes[i]
 
		return None
 

	
 
	## Create and return a new game tree containing the provided Node.
 
	#
 
	# Ancestor nodes are copied, descendants are moved from the seedNode.
 
	def _buildSubtree(self,seedNode):
 
		node=seedNode.copy()
 
		node.setChildren(seedNode.children)
 
		seedNode.children=[]
 

	
 
		while node.parent:
 
			newNode=node.parent.copy()
 
			newNode.addChild(node)
 
			node=newNode
 

	
 
		return node
src/diana/sgfParser/gameRecord.py
Show inline comments
 
file renamed from src/diana/gameRecord.py to src/diana/sgfParser/gameRecord.py
 
from sgfParser.node import Node
 
from .node import Node
 

	
 

	
 
## Wrapper around a Node tree.
 
class GameRecord:
 
	def __init__(self,root=None):
 
		self.root=root or Node()
 
		self._gameInfoNode=next(root.listGINodes())
 

	
 
	def export(self):
 
		return self.root.export()
 

	
 
	def set(self,name,value):
 
		self._gameInfoNode.setProp(name,value)
 

	
 
	def get(self,name,default=None):
 
		return self._gameInfoNode.getProp(name,default)
src/diana/tests/testSgfParser.py
Show inline comments
 
from itertools import chain
 
from datetime import date
 
import unittest
 
from unittest import TestCase
 
import os
 

	
 
from sgfParser import strRowCol
 
from sgfParser.collection import Collection
 
from sgfParser.property import Property,DateProperty,DateException
 
from sgfParser.propValues import text,compose
 
from diana.sgfParser import strRowCol
 
from diana.sgfParser.collection import Collection
 
from diana.sgfParser.property import Property,DateProperty,DateException
 
from diana.sgfParser.propValues import text,compose
 

	
 

	
 
dataDir=os.path.join(os.path.dirname(__file__), "data")
 

	
 

	
 
class TestUtils(TestCase):
 
	def testTextPos(self):
 
		s="abc\ndef\rgh\r\nij\n\rklmn"
 
		rc=[
 
			[1,2,3,4],
 
			[1,2,3,4],
 
			[1,2,3,4],
 
			[1,2,3], [1], # don't care about LFCR, we unicode now
 
			[1,2,3,4]
 
		]
 
		res=chain((r+1,c) for (r,row) in enumerate(rc) for c in row)
 
		for (i,(r,c)) in zip(range(len(s)+1), res):
 
			self.assertEqual(strRowCol(s, i), (r, c))
 

	
 

	
 
class TestProperty(TestCase):
 
	def testName(self):
 
		with self.assertRaises(AssertionError):
 
			Property.create("[99]",0)
 
		with self.assertRaises(AssertionError):
 
			Property.create("99[99]",0)
 

	
 
		i,prop=Property.create("MN[99]",0)
 
		self.assertNotEqual((i,prop), (0,None))
 
		self.assertEqual((i,prop.name), (6,"MN"))
 

	
 
	def testText(self):
 
		s=r"""[abc\
 
def
 
ghi]"""
 
		self.assertEqual(text()(s,1)[1], "abcdef ghi")
 
		self.assertEqual(text(False)(s,1)[1], "abcdef\nghi")
 

	
 
		s="""[m\\no\\\tpqr\\]\\\\]"""
 
		self.assertEqual(text()(s,1)[1], "mno pqr]\\")
 
		self.assertEqual(text(False)(s,1)[1], "mno pqr]\\")
 

	
 
		s="""[abc:def]"""
 
		parsed=compose(text(composed=True),text(composed=True))(s,1)
 
		self.assertEqual(str(parsed[1]), "abc:def")
 

	
 

	
 
class TestDateProperty(TestCase):
 
	def testSingle(self):
 
		self.assertEqual(DateProperty.parseSingle("2019","Y")[1], date(2019,1,1))
 
		self.assertEqual(DateProperty.parseSingle("2019-06","YM")[1], date(2019,6,1))
 
		self.assertEqual(DateProperty.parseSingle("2019-06-22","YMD")[1], date(2019,6,22))
 
		d=date(2019,6,21)
 
		self.assertEqual(DateProperty.parseSingle("22","D",d)[1], date(2019,6,22))
 
		self.assertEqual(DateProperty.parseSingle("07-22","MD",d)[1], date(2019,7,22))
 
		self.assertEqual(DateProperty.parseSingle("2020-07-22","YMD",d)[1], date(2020,7,22))
 
		with self.assertRaises(ValueError):
 
			DateProperty.parseSingle("2019-31","YMD")
 

	
 
	def testParse(self):
 
		self.assertEqual(DateProperty("1996-05,06").value, [date(1996,5,1),date(1996,6,1)])
 
		self.assertEqual(DateProperty("1996-05-06,07,08").value, [date(1996,5,6),date(1996,5,7),date(1996,5,8)])
 
		self.assertEqual(DateProperty("1996,1997").value, [date(1996,1,1),date(1997,1,1)])
 
		self.assertEqual(DateProperty("1996-12-27,28,1997-01-03,04").value, [date(1996,12,27),date(1996,12,28),date(1997,1,3),date(1997,1,4)])
 
		self.assertEqual(DateProperty("1997-05-05,1997-05-06").value, [date(1997,5,5),date(1997,5,6)])
 
		self.assertEqual(DateProperty("Published on 1997-05-06").value, [date(1997,5,6)])
 
		with self.assertRaises(DateException):
 
			DateProperty("unknown")
 

	
 

	
 
class TestCollection(TestCase):
 
	def testSubtrees(self):
 
		c=Collection("""
 
(;B[aa]
 
	(;W[ab]PB[Some Black]PW[Some White];B[ac])
 
	(;W[bb]PB[Other Black]PW[Other White])
 
)""")
 
		games=list(c.listGames())
 

	
 
		self.assertEqual(len(games),2)
 
		self.assertRegex(games[0].export(), r"^\(;B\[aa]\n;(PB\[Some Black]|PW\[Some White]|W\[ab]){3}\n;B\[ac]\n\)\n$")
 
		self.assertRegex(games[1].export(), r"^\(;B\[aa]\n;(PB\[Other Black]|PW\[Other White]|W\[bb]){3}\n\)\n$")
 

	
 
	def testEmptySgf(self):
 
		Collection("(;)")
 

	
 
	def testSimpleSgf(self):
 
		with open(os.path.join(dataDir, "simple.sgf")) as f:
 
			Collection(f.read())
 

	
 
	def testComplexSgf(self):
 
		with open(os.path.join(dataDir, "kogos.sgf")) as f:
 
			Collection(f.read())
 

	
 
if __name__ == '__main__':
 
	unittest.main()
0 comments (0 inline, 0 general)