Changeset - f90261363a85
[Not reviewed]
default
1 4 0
Laman - 9 years ago 2016-08-14 08:55:54

spaces to tabs
5 files changed with 1053 insertions and 1177 deletions:
diana.py
170
170
go.py
29
29
sgf.py
451
451
sgfParser.py
403
403
0 comments (0 inline, 0 general)
diana.py
Show inline comments
 
import sys
 
import re
 
import go
 
import os
 
import sgf
 
 
if len(sys.argv)>1:
 
  files=sys.argv[1:]
 
	files=sys.argv[1:]
 
else:
 
  sys.exit("no input file specified")
 
	sys.exit("no input file specified")
 
 
movesPerDiagram=75
 
minMovesPerDiagram=10
 
 
t=sys.stdout
 
c=28
 
padding=15
 
highNumbers=True
 
 
class Svg:
 
  content=""
 
  footer=""
 
  extension="svg"
 
	content=""
 
	footer=""
 
	extension="svg"
 
 
  padding=15
 
  gridSize=28
 
  highNumbers=True
 
  
 
  def __init__(self):
 
    self.content='''<?xml version="1.0" standalone="no"?>
 
	padding=15
 
	gridSize=28
 
	highNumbers=True
 
	
 
	def __init__(self):
 
		self.content='''<?xml version="1.0" standalone="no"?>
 
<svg width="{0}" height="{0}" version="1.1" xmlns="http://www.w3.org/2000/svg" alignment-baseline="center">
 
  <defs>
 
    <style type="text/css"><![CDATA[
 
    text{{font-family:"DejaVu Sans";text-anchor:middle;}}
 
    line{{stroke:black;stroke-width:0.7}}
 
    circle{{stroke:black}}
 
    .b{{fill:black}}
 
    .w{{fill:white}}
 
    ]]></style>
 
  </defs>
 
  <rect width="{0}" height="{0}" x="0" y="0" style="fill:white;stroke:white"/>\n'''.format(2*self.padding+18*self.gridSize)
 
    self.footer="</svg>\n"
 
	<defs>
 
		<style type="text/css"><![CDATA[
 
		text{{font-family:"DejaVu Sans";text-anchor:middle;}}
 
		line{{stroke:black;stroke-width:0.7}}
 
		circle{{stroke:black}}
 
		.b{{fill:black}}
 
		.w{{fill:white}}
 
		]]></style>
 
	</defs>
 
	<rect width="{0}" height="{0}" x="0" y="0" style="fill:white;stroke:white"/>\n'''.format(2*self.padding+18*self.gridSize)
 
		self.footer="</svg>\n"
 
 
    grid='  <line x1="{0}" x2="{1}" y1="{2}" y2="{3}" />\n'
 
		grid='  <line x1="{0}" x2="{1}" y1="{2}" y2="{3}" />\n'
 
 
    # okraje desky
 
    for i in (0,18):
 
      self.content+='  <line x1="{0}" x2="{1}" y1="{2}" y2="{3}" style="stroke-width:1"/>\n'.format(self.padding, 18*self.gridSize+self.padding, self.gridSize*i+self.padding, self.gridSize*i+self.padding)
 
      self.content+='  <line x1="{0}" x2="{1}" y1="{2}" y2="{3}" style="stroke-width:1"/>\n'.format(self.gridSize*i+self.padding, self.gridSize*i+self.padding, self.padding, 18*self.gridSize+self.padding)
 
    
 
    # mřížka
 
    for i in range(1,18):
 
      self.content+=grid.format(padding, 18*c+padding, c*i+padding, c*i+padding)
 
      self.content+=grid.format(c*i+padding, c*i+padding, padding, 18*c+padding)
 
		# okraje desky
 
		for i in (0,18):
 
			self.content+='  <line x1="{0}" x2="{1}" y1="{2}" y2="{3}" style="stroke-width:1"/>\n'.format(self.padding, 18*self.gridSize+self.padding, self.gridSize*i+self.padding, self.gridSize*i+self.padding)
 
			self.content+='  <line x1="{0}" x2="{1}" y1="{2}" y2="{3}" style="stroke-width:1"/>\n'.format(self.gridSize*i+self.padding, self.gridSize*i+self.padding, self.padding, 18*self.gridSize+self.padding)
 
		
 
		# mřížka
 
		for i in range(1,18):
 
			self.content+=grid.format(padding, 18*c+padding, c*i+padding, c*i+padding)
 
			self.content+=grid.format(c*i+padding, c*i+padding, padding, 18*c+padding)
 
 
    # hvězdy
 
    for i in range(3):
 
      for j in range(3):
 
        self.content+='  <circle cx="{0}" cy="{1}" r="{2}" class="b"/>\n'.format(padding+3*c+6*i*c, padding+3*c+6*j*c, 2)
 
  
 
  def __str__(self):
 
    return self.content+self.footer
 
          
 
  def drawStone(self,x,y,color):
 
    self.content+='  <circle cx="{0}" cy="{1}" r="{2}" class="{3}" />\n'.format(padding+x*c, padding+y*c, c/2-1, color)
 
		# hvězdy
 
		for i in range(3):
 
			for j in range(3):
 
				self.content+='  <circle cx="{0}" cy="{1}" r="{2}" class="b"/>\n'.format(padding+3*c+6*i*c, padding+3*c+6*j*c, 2)
 
	
 
	def __str__(self):
 
		return self.content+self.footer
 
					
 
	def drawStone(self,x,y,color):
 
		self.content+='  <circle cx="{0}" cy="{1}" r="{2}" class="{3}" />\n'.format(padding+x*c, padding+y*c, c/2-1, color)
 
 
  def getFontSize(self,text):
 
    if len(text)<2: return round(0.7*c)
 
    elif len(text)<3: return round(0.55*c)
 
    else: return round(0.4*c)
 
	def getFontSize(self,text):
 
		if len(text)<2: return round(0.7*c)
 
		elif len(text)<3: return round(0.55*c)
 
		else: return round(0.4*c)
 
 
  def writeLabel(self,x,y,label,color):
 
    label=str(label)
 
    fontSize=self.getFontSize(label)
 
    self.content+='  <text x="{0}" y="{1}" class="{2}" font-size="{3}">{4}</text>\n'.format(padding+x*c, padding+y*c+0.35*fontSize, color, fontSize, label)
 
    
 
  def drawMove(self,x,y,label,color):
 
    labelColor="w" if color=="b" else "b"
 
    
 
    if (not self.highNumbers) and isinstance(label,int) and label%100!=0: label=label%100 # dost neobratná logika
 
    
 
    self.drawStone(x,y,color)
 
    self.writeLabel(x,y,label,labelColor)
 
    
 
  def getContent(self):
 
    return self.content+self.footer
 
	def writeLabel(self,x,y,label,color):
 
		label=str(label)
 
		fontSize=self.getFontSize(label)
 
		self.content+='  <text x="{0}" y="{1}" class="{2}" font-size="{3}">{4}</text>\n'.format(padding+x*c, padding+y*c+0.35*fontSize, color, fontSize, label)
 
		
 
	def drawMove(self,x,y,label,color):
 
		labelColor="w" if color=="b" else "b"
 
		
 
		if (not self.highNumbers) and isinstance(label,int) and label%100!=0: label=label%100 # dost neobratná logika
 
		
 
		self.drawStone(x,y,color)
 
		self.writeLabel(x,y,label,labelColor)
 
		
 
	def getContent(self):
 
		return self.content+self.footer
 
 
 
class Tikz:
 
  content=""
 
  footer=""
 
  extension="tex"
 
  
 
  highNumbers=True
 
  
 
  def __init__(self):
 
    self.content=r'''\begin{tikzpicture}
 
  \draw[step=\boardSquare,gray,very thin] (0,0) grid (18\boardSquare,18\boardSquare);
 
  \draw (0,0) rectangle (18\boardSquare,18\boardSquare);
 
  
 
  '''
 
    
 
    # hvězdy
 
    for i in range(3):
 
      for j in range(3):
 
        self.content+=r'''  \filldraw[fill=black] ({0}\boardSquare,{1}\boardSquare) circle[radius=0.04];'''.format(6*i+3, 6*j+3)+'\n'
 
      self.content+='\n'
 
  
 
    self.footer=r'\end{tikzpicture}' '\n'
 
      
 
  def __str__(self):
 
    return self.content+self.footer
 
	content=""
 
	footer=""
 
	extension="tex"
 
	
 
	highNumbers=True
 
	
 
	def __init__(self):
 
		self.content=r'''\begin{tikzpicture}
 
	\draw[step=\boardSquare,gray,very thin] (0,0) grid (18\boardSquare,18\boardSquare);
 
	\draw (0,0) rectangle (18\boardSquare,18\boardSquare);
 
	
 
	'''
 
		
 
		# hvězdy
 
		for i in range(3):
 
			for j in range(3):
 
				self.content+=r'''  \filldraw[fill=black] ({0}\boardSquare,{1}\boardSquare) circle[radius=0.04];'''.format(6*i+3, 6*j+3)+'\n'
 
			self.content+='\n'
 
	
 
		self.footer=r'\end{tikzpicture}' '\n'
 
			
 
	def __str__(self):
 
		return self.content+self.footer
 
 
  def drawStone(self,x,y,color):
 
    fill="black" if color=="b" else "white"
 
    self.content+=r'  \filldraw[draw=black,fill={0}] ({1}\boardSquare,{2}\boardSquare) circle[radius=0.5\boardSquare];'.format(fill,x,18-y)+'\n'
 
  
 
  def drawMove(self,x,y,label,color):
 
    fill="black" if color=="b" else "white"
 
    labelColor="white" if color=="b" else "black"
 
    if (not self.highNumbers) and isinstance(label,int) and label%100!=0: label=label%100 # dost neobratná logika
 
    
 
    self.content+=r'  \filldraw[draw=black,fill={0}] ({1}\boardSquare,{2}\boardSquare) circle[radius=0.5\boardSquare] node[color={3}]{{{4}}};'.format(fill,x,18-y,labelColor,label)+'\n'
 
    
 
  def getContent(self):
 
    return self.content+self.footer
 
	def drawStone(self,x,y,color):
 
		fill="black" if color=="b" else "white"
 
		self.content+=r'  \filldraw[draw=black,fill={0}] ({1}\boardSquare,{2}\boardSquare) circle[radius=0.5\boardSquare];'.format(fill,x,18-y)+'\n'
 
	
 
	def drawMove(self,x,y,label,color):
 
		fill="black" if color=="b" else "white"
 
		labelColor="white" if color=="b" else "black"
 
		if (not self.highNumbers) and isinstance(label,int) and label%100!=0: label=label%100 # dost neobratná logika
 
		
 
		self.content+=r'  \filldraw[draw=black,fill={0}] ({1}\boardSquare,{2}\boardSquare) circle[radius=0.5\boardSquare] node[color={3}]{{{4}}};'.format(fill,x,18-y,labelColor,label)+'\n'
 
		
 
	def getContent(self):
 
		return self.content+self.footer
 
 
 
def processFile(fileName):
 
  shortName="".join(re.split(r'[/\\]',fileName)[-1].split('.')[:-1])
 
  
 
  game=go.Go()
 
  global t
 
  
 
  record=sgf.Sgf(open(fileName,'r',encoding="utf-8").read())
 
  moves=record.getMoves()
 
	shortName="".join(re.split(r'[/\\]',fileName)[-1].split('.')[:-1])
 
	
 
	game=go.Go()
 
	global t
 
	
 
	record=sgf.Sgf(open(fileName,'r',encoding="utf-8").read())
 
	moves=record.getMoves()
 
 
  localBoard=dict()
 
  overlays=""
 
  letters=dict()
 
  letter='a'
 
	localBoard=dict()
 
	overlays=""
 
	letters=dict()
 
	letter='a'
 
 
  diagramsNeeded=(len(moves)-minMovesPerDiagram)//movesPerDiagram+1
 
  moveNumber=0
 
  
 
  for i in range(diagramsNeeded):
 
    # inicializuj diagram
 
    diagram=Tikz()
 
    
 
    for lineNumber,line in enumerate(game.board):
 
      for itemNumber,item in enumerate(line):
 
        if item==1: diagram.drawStone(itemNumber,lineNumber,"b")
 
        if item==-1: diagram.drawStone(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]!=0}
 
    
 
    for j in range(movesPerDiagram):
 
      # kresli tahy
 
      if moveNumber>=len(moves): break
 
      
 
      c,(x,y)=moves[moveNumber]
 
      c=c.lower()
 
      
 
      if not game.move(1 if c=='b' else -1,x,y):
 
        print("illegal move: {0} at {1},{2}".format(moveNumber+1,x,y))
 
        moveNumber+=1
 
        continue
 
      
 
      # zapíšu tah na volný průsečík
 
      if not (x,y) in localBoard:
 
        localBoard[(x,y)]=moveNumber+1
 
        diagram.drawMove(x,y,moveNumber+1,c)
 
      # průsečík je obsazený nepopsaným kamenem
 
      elif localBoard[(x,y)]<1:
 
        # průsečík není popsaný ani písmenem
 
        if not (x,y) in letters:
 
          letters[(x,y)]=letter
 
          color='b' if localBoard[(x,y)]==0 else 'w'
 
          diagram.drawMove(x,y,letter,color)
 
          letter=chr(ord(letter)+1)
 
        overlays+="{0} = {1}\n".format(moveNumber+1,letters[(x,y)])
 
      # průsečík je obsazený očíslovaným kamenem
 
      else: overlays+="{0} = {1}\n".format(moveNumber+1,localBoard[(x,y)])
 
      
 
      moveNumber+=1
 
      
 
    # dokonči a ulož diagram
 
	diagramsNeeded=(len(moves)-minMovesPerDiagram)//movesPerDiagram+1
 
	moveNumber=0
 
	
 
	for i in range(diagramsNeeded):
 
		# inicializuj diagram
 
		diagram=Tikz()
 
		
 
		for lineNumber,line in enumerate(game.board):
 
			for itemNumber,item in enumerate(line):
 
				if item==1: diagram.drawStone(itemNumber,lineNumber,"b")
 
				if item==-1: diagram.drawStone(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]!=0}
 
		
 
		for j in range(movesPerDiagram):
 
			# kresli tahy
 
			if moveNumber>=len(moves): break
 
			
 
			c,(x,y)=moves[moveNumber]
 
			c=c.lower()
 
			
 
			if not game.move(1 if c=='b' else -1,x,y):
 
				print("illegal move: {0} at {1},{2}".format(moveNumber+1,x,y))
 
				moveNumber+=1
 
				continue
 
			
 
			# zapíšu tah na volný průsečík
 
			if not (x,y) in localBoard:
 
				localBoard[(x,y)]=moveNumber+1
 
				diagram.drawMove(x,y,moveNumber+1,c)
 
			# průsečík je obsazený nepopsaným kamenem
 
			elif localBoard[(x,y)]<1:
 
				# průsečík není popsaný ani písmenem
 
				if not (x,y) in letters:
 
					letters[(x,y)]=letter
 
					color='b' if localBoard[(x,y)]==0 else 'w'
 
					diagram.drawMove(x,y,letter,color)
 
					letter=chr(ord(letter)+1)
 
				overlays+="{0} = {1}\n".format(moveNumber+1,letters[(x,y)])
 
			# průsečík je obsazený očíslovaným kamenem
 
			else: overlays+="{0} = {1}\n".format(moveNumber+1,localBoard[(x,y)])
 
			
 
			moveNumber+=1
 
			
 
		# dokonči a ulož diagram
 
	# TODO rozumně pracovat s adresáři
 
    t=open(os.path.join(os.path.dirname(__file__),"out","{0}-{1}.{2}".format(shortName,i+1,diagram.extension)),'w') # nový soubor
 
    t.write(diagram.getContent())
 
    t.close()
 
      
 
  notes=open(os.path.join(os.path.dirname(__file__),"out","{0}.txt".format(shortName)),'w')
 
  notes.write(overlays)
 
  notes.close()
 
  
 
		t=open(os.path.join(os.path.dirname(__file__),"out","{0}-{1}.{2}".format(shortName,i+1,diagram.extension)),'w') # nový soubor
 
		t.write(diagram.getContent())
 
		t.close()
 
			
 
	notes=open(os.path.join(os.path.dirname(__file__),"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))
 
	# 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))
go.py
Show inline comments
 
class Go:
 
  # 1: B, 0: _, -1: W
 
  # resp. jakákoli čísla s opačnými znaménky
 
  board=[[0]*19 for i in range(19)]
 
  
 
  def __init__(self): self.board=[[0]*19 for i in range(19)]
 
  
 
  def move(self,color,y,x):
 
    if self.board[x][y]!=0: return False
 
	# 1: B, 0: _, -1: W
 
	# resp. jakákoli čísla s opačnými znaménky
 
	board=[[0]*19 for i in range(19)]
 
	
 
	def __init__(self): self.board=[[0]*19 for i in range(19)]
 
	
 
	def move(self,color,y,x):
 
		if self.board[x][y]!=0: return False
 
 
    self.board[x][y]=color
 
		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]=0
 
      return False
 
    return True
 
		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]=0
 
			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]==0: 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]=0
 
	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]==0: 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]=0
sgf-compresor.py
Show inline comments
 
deleted file
sgf.py
Show inline comments
 
import re
 
 
 
class Collection:
 
  gameTrees=[]
 
  
 
  def export(self):
 
    res=""
 
    for root in gameTrees:
 
      res+="({0})\n".format(root.fullExport())
 
    return res
 
  
 
	gameTrees=[]
 
	
 
	def export(self):
 
		res=""
 
		for root in gameTrees:
 
			res+="({0})\n".format(root.fullExport())
 
		return res
 
	
 
class Node:
 
  parent=None
 
  children=[]
 
  properties={}
 
  
 
  def newChild(self):
 
    child=Node()
 
    child.parent=self
 
    self.children.append(child)
 
    return child
 
    
 
  def addChild(self,child):
 
    child.parent=self
 
    self.children.append(child)
 
    
 
  def delChild(self,index):
 
    del self.children[index]
 
    
 
  def setProperty(self): pass
 
  def getProperty(self): pass
 
  
 
  def fullExport(self):
 
    res=self.export()
 
    if len(self.children)==1: res+=children[0].fullExport()
 
    else:
 
      for child in self.children:
 
        res+=child.fullExport()
 
    return res
 
      
 
  def export(self):
 
    res=";"
 
    for name,value in properties.items():
 
      res+="{0}[{1}]".format(name,value) # !! str(value)
 
    res+="\n"
 
    return res
 
    
 
	parent=None
 
	children=[]
 
	properties={}
 
	
 
	def newChild(self):
 
		child=Node()
 
		child.parent=self
 
		self.children.append(child)
 
		return child
 
		
 
	def addChild(self,child):
 
		child.parent=self
 
		self.children.append(child)
 
		
 
	def delChild(self,index):
 
		del self.children[index]
 
		
 
	def setProperty(self): pass
 
	def getProperty(self): pass
 
	
 
	def fullExport(self):
 
		res=self.export()
 
		if len(self.children)==1: res+=children[0].fullExport()
 
		else:
 
			for child in self.children:
 
				res+=child.fullExport()
 
		return res
 
			
 
	def export(self):
 
		res=";"
 
		for name,value in properties.items():
 
			res+="{0}[{1}]".format(name,value) # !! str(value)
 
		res+="\n"
 
		return res
 
		
 
class Property:
 
  name=""
 
  value=None
 
	name=""
 
	value=None
 
 
class ListOf(Property):
 
  values=[]
 
  vType=None
 
	values=[]
 
	vType=None
 
 
  def __str__(self):
 
    return "["+"][".join(self.values)+"]"
 
    
 
	def __str__(self):
 
		return "["+"][".join(self.values)+"]"
 
		
 
class Composed(Property):
 
  value=()
 
  
 
  def __str__(self):
 
    return self.value[0]+":"+self.value[1] # !! str(self.value[0])
 
	value=()
 
	
 
	def __str__(self):
 
		return self.value[0]+":"+self.value[1] # !! str(self.value[0])
 
 
class Point(Property): # !! pass
 
  row=0
 
  col=0
 
  
 
  def __str__(self):
 
    f=lambda x: chr(x+ord("a")) if x<26 else chr(x-26+ord("A"))
 
    row=f(self.row)
 
    col=f(self.col)
 
    
 
    return col+row
 
    
 
	row=0
 
	col=0
 
	
 
	def __str__(self):
 
		f=lambda x: chr(x+ord("a")) if x<26 else chr(x-26+ord("A"))
 
		row=f(self.row)
 
		col=f(self.col)
 
		
 
		return col+row
 
		
 
 
  
 
	
 
def skipWhitespace(str,start):
 
  i=start
 
  while i<len(str) and str[i].isspace(): i+=1
 
  return i
 
	i=start
 
	while i<len(str) and str[i].isspace(): i+=1
 
	return i
 
 
class Collection:
 
  gameTrees=[]
 
  
 
  def __init__(self,str):
 
    self.gameTrees=[]
 
    i,x=GameTree.create(str,0)
 
    if x is None:
 
      print("error when parsing Collection")
 
      return
 
    while x is not None:
 
      self.gameTrees.append(x)
 
      i,x=GameTree.create(str,i)
 
  
 
	gameTrees=[]
 
	
 
	def __init__(self,str):
 
		self.gameTrees=[]
 
		i,x=GameTree.create(str,0)
 
		if x is None:
 
			print("error when parsing Collection")
 
			return
 
		while x is not None:
 
			self.gameTrees.append(x)
 
			i,x=GameTree.create(str,i)
 
	
 
class GameTree:
 
  nodes=[]
 
  branches=[]
 
    
 
  def create(str,start):
 
    res=GameTree()
 
    i=skipWhitespace(str,start)
 
    if i>=len(str) or str[i]!="(":
 
      # print("error when parsing GameTree")
 
      return (start,None)
 
    i=skipWhitespace(str,i)
 
    i,x=Node.create(str,start+1)
 
    if x is None:
 
      # print("error when parsing GameTree")
 
      return (i,None)
 
    while x is not None:
 
      res.nodes.append(x)
 
      i=skipWhitespace(str,i)
 
      i,x=Node.create(str,i)
 
    i=skipWhitespace(str,i)
 
    i,x=GameTree.create(str,i)
 
    while x is not None:
 
      res.branches.append(x)
 
      i=skipWhitespace(str,i)
 
      i,x=GameTree.create(str,i)
 
    if str[i]!=")":
 
      # print("error when parsing GameTree")
 
      return (i,None)
 
    return (i+1,res)
 
  
 
	nodes=[]
 
	branches=[]
 
		
 
	def create(str,start):
 
		res=GameTree()
 
		i=skipWhitespace(str,start)
 
		if i>=len(str) or str[i]!="(":
 
			# print("error when parsing GameTree")
 
			return (start,None)
 
		i=skipWhitespace(str,i)
 
		i,x=Node.create(str,start+1)
 
		if x is None:
 
			# print("error when parsing GameTree")
 
			return (i,None)
 
		while x is not None:
 
			res.nodes.append(x)
 
			i=skipWhitespace(str,i)
 
			i,x=Node.create(str,i)
 
		i=skipWhitespace(str,i)
 
		i,x=GameTree.create(str,i)
 
		while x is not None:
 
			res.branches.append(x)
 
			i=skipWhitespace(str,i)
 
			i,x=GameTree.create(str,i)
 
		if str[i]!=")":
 
			# print("error when parsing GameTree")
 
			return (i,None)
 
		return (i+1,res)
 
	
 
class Node:
 
  properties=dict()
 
  
 
  def create(str,start):
 
    res=Node()
 
    if str[start]!=";":
 
      # print("error when parsing Node")
 
      return (start,None)
 
    i=skipWhitespace(str,start+1)
 
    i,x=Property.create(str,i)
 
    while x is not None:
 
      if x.name in res.properties:
 
        print('error: duplicate "{0}" property in node at position {1}. second value ignored'.format(x.name,start))
 
      else:
 
        res.properties[x.name]=x
 
      i=skipWhitespace(str,i)
 
      i,x=Property.create(str,i)
 
    return (i,res)
 
    
 
  def setProperty(self,name,value):
 
    self.properties[name]=value
 
    # zkontrolovat typ value
 
    
 
  def getProperty(self,name):
 
    if name in self.properties: return self.properties[name]
 
    else: return None
 
  
 
	properties=dict()
 
	
 
	def create(str,start):
 
		res=Node()
 
		if str[start]!=";":
 
			# print("error when parsing Node")
 
			return (start,None)
 
		i=skipWhitespace(str,start+1)
 
		i,x=Property.create(str,i)
 
		while x is not None:
 
			if x.name in res.properties:
 
				print('error: duplicate "{0}" property in node at position {1}. second value ignored'.format(x.name,start))
 
			else:
 
				res.properties[x.name]=x
 
			i=skipWhitespace(str,i)
 
			i,x=Property.create(str,i)
 
		return (i,res)
 
		
 
	def setProperty(self,name,value):
 
		self.properties[name]=value
 
		# zkontrolovat typ value
 
		
 
	def getProperty(self,name):
 
		if name in self.properties: return self.properties[name]
 
		else: return None
 
	
 
class Property:
 
  name=""
 
  value=""
 
  
 
  def create(str,start):
 
    res=Property()
 
    i,x=Property.ident(str,start)
 
    if x is None:
 
      return (start,None)
 
    res.name=x
 
    i,x=PropValue.create(str,i,res.name)
 
    if x is None:
 
      print('error when parsing property "{0}" at position {1}'.format(res.name,i))
 
      return (start,None)
 
    # while x is not None: # přesunuto do PropValue.listOf
 
      # res.values.append(x)
 
      # i=skipWhitespace(str,i)
 
      # i,x=PropValue.create(str,i,res.name)
 
    res.values.append(x) # !! podezřelé
 
    return (i,res)
 
    
 
  def ident(str,start):
 
    r=re.compile(r"[A-Z]+")
 
    m=r.match(str,start)
 
    if m is None: return (start,None)
 
    return (m.end(),m.group())
 
	name=""
 
	value=""
 
	
 
	def create(str,start):
 
		res=Property()
 
		i,x=Property.ident(str,start)
 
		if x is None:
 
			return (start,None)
 
		res.name=x
 
		i,x=PropValue.create(str,i,res.name)
 
		if x is None:
 
			print('error when parsing property "{0}" at position {1}'.format(res.name,i))
 
			return (start,None)
 
		# while x is not None: # přesunuto do PropValue.listOf
 
			# res.values.append(x)
 
			# i=skipWhitespace(str,i)
 
			# i,x=PropValue.create(str,i,res.name)
 
		res.values.append(x) # !! podezřelé
 
		return (i,res)
 
		
 
	def ident(str,start):
 
		r=re.compile(r"[A-Z]+")
 
		m=r.match(str,start)
 
		if m is None: return (start,None)
 
		return (m.end(),m.group())
 
 
class PropValue:
 
  type=""
 
  value=None
 
  patterns=dict()
 
  
 
  def create(str,start,name):
 
    if name in PropValue.patterns:
 
      return PropValue.patterns[name](str,start)
 
    else:
 
      print('warning, unknown property "{0}" at position {1}'.format(name,start))
 
      return PropValue.singleton(PropValue.anything)(str,start)
 
  
 
  # def singleton(str,start,vType):
 
    # if str[start]!="[":
 
      # return (start,None)
 
    # i,x=vType(str,start+1)
 
    # if x is None: return (start,None)
 
    # if str[i]!="]":
 
      # return (start,None)
 
    # return (i+1,x)
 
  
 
  def choose(*vTypes):
 
    def f(str,start):
 
      for vType in vTypes:
 
        i,x=vType(str,start)
 
        if x is not None: return (i,x)
 
      return (start,None)
 
    return f
 
  
 
  def singleton(vType):
 
    def f(str,start):
 
      if str[start]!="[":
 
        return (start,None)
 
      i,x=vType(str,start+1)
 
      if x is None: return (start,None)
 
      if str[i]!="]":
 
        return (start,None)
 
      return (i+1,x)
 
    return f
 
    
 
  # def listOf(str,start,vType,allowEmpty=False):
 
    # res=[]
 
    # i,x=singleton(str,start,vType)
 
    # # singleton(vType) if vType not tuple else compose(vType[0],vType[1])
 
    # while x!=None:
 
      # res.append(x)
 
      # i,x=singleton(str,i,vType)
 
    # if len(res)==0 and not allowEmpty: return (start,None)
 
    # return (i,res)
 
    
 
  def listOf(vType,allowEmpty=False):
 
    def f(str,start):
 
      res=[]
 
      single=singleton(vType)
 
      i,x=single(str,start)
 
      while x!=None:
 
        res.append(x)
 
        i,x=single(str,i)
 
      if len(res)==0 and not allowEmpty: return (start,None)
 
      return (i,res)
 
    return f
 
    
 
  # def compose(str,start,vTypeA,vTypeB):
 
    # i,a=vTypeA(str,start)
 
    # if a==None or str[i]!=":": return (start,None)
 
    # i,b=vTypeB(str,i+1)
 
    # if b==None: return start,None
 
    # return (i,(a,b))
 
    
 
  def compose(vTypeA,vTypeB):
 
    def f(str,start):
 
      i,a=vTypeA(str,start)
 
      # print(">",i,a)
 
      if a==None or str[i]!=":": return (start,None)
 
      i,b=vTypeB(str,i+1)
 
      # print(">",i,b)
 
      if b==None: return start,None
 
      return (i,(a,b))
 
    return f
 
  
 
  def number(str,start):
 
    r=re.compile(r"(\+|-|)\d+")
 
    m=r.match(str,start)
 
    if m is None: return (start,None)
 
    res=int(m.group(0))
 
    return (m.end(),res)
 
    
 
  def real(str,start):
 
    r=re.compile(r"(\+|-|)\d+(\.\d+)?")
 
    m=r.match(str,start)
 
    if m is None: return (start,None)
 
    res=float(m.group(0))
 
    return (m.end(),res)
 
    
 
  def double(str,start):
 
    r=re.compile(r"1|2")
 
    m=r.match(str,start)
 
    if m is None: return (start,None)
 
    res=int(m.group(0))
 
    return (m.end(),res)
 
  
 
  def color(str,start):
 
    r=re.compile(r"B|W")
 
    m=r.match(str,start)
 
    if m is None: return (start,None)
 
    return (m.end(),m.group(0))
 
    
 
  # def simpleText(str,start):
 
    # res=""
 
    # esc=False
 
    # lineBreak=False
 
    # for c in str:
 
      # if esc:
 
        # res+=c
 
        # esc=False
 
      # elif c=="\\":
 
        # esc=True
 
      # elif c=="]":
 
        # break
 
      # else:
 
        # res+=c
 
    # return res
 
  
 
  def text(simple=True,composed=False):
 
    def f(str,start):
 
      res=""
 
      esc=False
 
      lastC=""
 
      for i,c in enumerate(str[start:],start):
 
        if esc:
 
          if c!="\n" and c!="\r": res+=c
 
          esc=False
 
        elif (c=="\n" and lastC=="\r") or (c=="\r" and lastC=="\n"): pass
 
        elif c=="\r" or c=="\n" and not simple:
 
          res+="\n"
 
        elif c.isspace():
 
          res+=" "
 
        elif c=="\\":
 
          esc=True
 
        elif c=="]" or (c==":" and composed):
 
          break
 
        else:
 
          res+=c
 
        lastC=c
 
      return (i,res)
 
    return f
 
    
 
  def empty(str,start): return (start,"")
 
  
 
  def anything(str,start): # přidat listOf
 
    esc=False
 
    for i,c in enumerate(str[start:],start):
 
      if esc: esc=False
 
      elif c=="\\": esc=True
 
      elif c=="]": break
 
    return (i,str[start:i])
 
  
 
  # go specific
 
  def point(str,start):
 
    r=re.compile(r"[a-zA-Z]{2}|") # !! limit to board size
 
    m=r.match(str,start)
 
    if m is None: return (start,None)
 
    if m.group(0)=="": # pass, !! tt
 
      return (m.end(),tuple())
 
    col=m.group(0)[0]
 
    row=m.group(0)[1]
 
    col=ord(col) - (ord("a") if "a"<=col<="z" else ord("A")-26)
 
    row=ord(row) - (ord("a") if "a"<=row<="z" else ord("A")-26)
 
    return (m.end(),(col,row))
 
	type=""
 
	value=None
 
	patterns=dict()
 
	
 
	def create(str,start,name):
 
		if name in PropValue.patterns:
 
			return PropValue.patterns[name](str,start)
 
		else:
 
			print('warning, unknown property "{0}" at position {1}'.format(name,start))
 
			return PropValue.singleton(PropValue.anything)(str,start)
 
	
 
	# def singleton(str,start,vType):
 
		# if str[start]!="[":
 
			# return (start,None)
 
		# i,x=vType(str,start+1)
 
		# if x is None: return (start,None)
 
		# if str[i]!="]":
 
			# return (start,None)
 
		# return (i+1,x)
 
	
 
	def choose(*vTypes):
 
		def f(str,start):
 
			for vType in vTypes:
 
				i,x=vType(str,start)
 
				if x is not None: return (i,x)
 
			return (start,None)
 
		return f
 
	
 
	def singleton(vType):
 
		def f(str,start):
 
			if str[start]!="[":
 
				return (start,None)
 
			i,x=vType(str,start+1)
 
			if x is None: return (start,None)
 
			if str[i]!="]":
 
				return (start,None)
 
			return (i+1,x)
 
		return f
 
		
 
	# def listOf(str,start,vType,allowEmpty=False):
 
		# res=[]
 
		# i,x=singleton(str,start,vType)
 
		# # singleton(vType) if vType not tuple else compose(vType[0],vType[1])
 
		# while x!=None:
 
			# res.append(x)
 
			# i,x=singleton(str,i,vType)
 
		# if len(res)==0 and not allowEmpty: return (start,None)
 
		# return (i,res)
 
		
 
	def listOf(vType,allowEmpty=False):
 
		def f(str,start):
 
			res=[]
 
			single=singleton(vType)
 
			i,x=single(str,start)
 
			while x!=None:
 
				res.append(x)
 
				i,x=single(str,i)
 
			if len(res)==0 and not allowEmpty: return (start,None)
 
			return (i,res)
 
		return f
 
		
 
	# def compose(str,start,vTypeA,vTypeB):
 
		# i,a=vTypeA(str,start)
 
		# if a==None or str[i]!=":": return (start,None)
 
		# i,b=vTypeB(str,i+1)
 
		# if b==None: return start,None
 
		# return (i,(a,b))
 
		
 
	def compose(vTypeA,vTypeB):
 
		def f(str,start):
 
			i,a=vTypeA(str,start)
 
			# print(">",i,a)
 
			if a==None or str[i]!=":": return (start,None)
 
			i,b=vTypeB(str,i+1)
 
			# print(">",i,b)
 
			if b==None: return start,None
 
			return (i,(a,b))
 
		return f
 
	
 
	def number(str,start):
 
		r=re.compile(r"(\+|-|)\d+")
 
		m=r.match(str,start)
 
		if m is None: return (start,None)
 
		res=int(m.group(0))
 
		return (m.end(),res)
 
		
 
	def real(str,start):
 
		r=re.compile(r"(\+|-|)\d+(\.\d+)?")
 
		m=r.match(str,start)
 
		if m is None: return (start,None)
 
		res=float(m.group(0))
 
		return (m.end(),res)
 
		
 
	def double(str,start):
 
		r=re.compile(r"1|2")
 
		m=r.match(str,start)
 
		if m is None: return (start,None)
 
		res=int(m.group(0))
 
		return (m.end(),res)
 
	
 
	def color(str,start):
 
		r=re.compile(r"B|W")
 
		m=r.match(str,start)
 
		if m is None: return (start,None)
 
		return (m.end(),m.group(0))
 
		
 
	# def simpleText(str,start):
 
		# res=""
 
		# esc=False
 
		# lineBreak=False
 
		# for c in str:
 
			# if esc:
 
				# res+=c
 
				# esc=False
 
			# elif c=="\\":
 
				# esc=True
 
			# elif c=="]":
 
				# break
 
			# else:
 
				# res+=c
 
		# return res
 
	
 
	def text(simple=True,composed=False):
 
		def f(str,start):
 
			res=""
 
			esc=False
 
			lastC=""
 
			for i,c in enumerate(str[start:],start):
 
				if esc:
 
					if c!="\n" and c!="\r": res+=c
 
					esc=False
 
				elif (c=="\n" and lastC=="\r") or (c=="\r" and lastC=="\n"): pass
 
				elif c=="\r" or c=="\n" and not simple:
 
					res+="\n"
 
				elif c.isspace():
 
					res+=" "
 
				elif c=="\\":
 
					esc=True
 
				elif c=="]" or (c==":" and composed):
 
					break
 
				else:
 
					res+=c
 
				lastC=c
 
			return (i,res)
 
		return f
 
		
 
	def empty(str,start): return (start,"")
 
	
 
	def anything(str,start): # přidat listOf
 
		esc=False
 
		for i,c in enumerate(str[start:],start):
 
			if esc: esc=False
 
			elif c=="\\": esc=True
 
			elif c=="]": break
 
		return (i,str[start:i])
 
	
 
	# go specific
 
	def point(str,start):
 
		r=re.compile(r"[a-zA-Z]{2}|") # !! limit to board size
 
		m=r.match(str,start)
 
		if m is None: return (start,None)
 
		if m.group(0)=="": # pass, !! tt
 
			return (m.end(),tuple())
 
		col=m.group(0)[0]
 
		row=m.group(0)[1]
 
		col=ord(col) - (ord("a") if "a"<=col<="z" else ord("A")-26)
 
		row=ord(row) - (ord("a") if "a"<=row<="z" else ord("A")-26)
 
		return (m.end(),(col,row))
 
 
  move=point
 
  stone=point
 
  
 
  patterns={
 
    "B":singleton(move),
 
    "KO":singleton(empty),
 
    "MN":singleton(number),
 
    "W":singleton(move),
 
    "AB":listOf(stone), #
 
    "AE":listOf(point), #
 
    "AW":listOf(stone), #
 
    "PL":singleton(color),
 
    "C":singleton(text(simple=False)),
 
    "DM":singleton(double),
 
    "GB":singleton(double),
 
    "GW":singleton(double),
 
    "HO":singleton(double),
 
    "N":singleton(text()),
 
    "UC":singleton(double),
 
    "V":singleton(real),
 
    "BM":singleton(double),
 
    "DO":singleton(empty),
 
    "IT":singleton(empty),
 
    "TE":singleton(double),
 
    "AR":listOf(compose(point,point)), #
 
    "CR":listOf(point), #
 
    "DD":listOf(point,allowEmpty=True), #
 
    "LB":listOf(compose(point,text())), #
 
    "LN":listOf(compose(point,point)), #
 
    "MA":listOf(point), #
 
    "SL":listOf(point), #
 
    "SQ":listOf(point), #
 
    "TR":listOf(point), #
 
    "AP":singleton(compose(text(composed=True),text())), #
 
    "CA":singleton(text()),
 
    "FF":singleton(number),
 
    "GM":singleton(number),
 
    "ST":singleton(number),
 
    "SZ":choose(singleton(number),singleton(compose(number,number))), #
 
    "AN":singleton(text()),
 
    "BR":singleton(text()),
 
    "BT":singleton(text()),
 
    "CP":singleton(text()),
 
    "DT":singleton(text()),
 
    "EV":singleton(text()),
 
    "GN":singleton(text()),
 
    "GC":singleton(text(simple=False)),
 
    "ON":singleton(text()),
 
    "OT":singleton(text()),
 
    "PB":singleton(text()),
 
    "PC":singleton(text()),
 
    "PW":singleton(text()),
 
    "RE":singleton(text()),
 
    "RO":singleton(text()),
 
    "RU":singleton(text()),
 
    "SO":singleton(text()),
 
    "TM":singleton(real),
 
    "US":singleton(text()),
 
    "WR":singleton(text()),
 
    "WT":singleton(text()),
 
    "BL":singleton(real),
 
    "OB":singleton(number),
 
    "OW":singleton(number),
 
    "WL":singleton(real),
 
    "FG":choose(singleton(empty),singleton(compose(number,text()))), #
 
    "PM":singleton(number),
 
    "VW":listOf(point,allowEmpty=True), #
 
    
 
    # go specific
 
    "HA":singleton(number),
 
    "KM":singleton(real),
 
    "TB":listOf(point,allowEmpty=True),
 
    "TW":listOf(point,allowEmpty=True)
 
  }
 
	move=point
 
	stone=point
 
	
 
	patterns={
 
		"B":singleton(move),
 
		"KO":singleton(empty),
 
		"MN":singleton(number),
 
		"W":singleton(move),
 
		"AB":listOf(stone), #
 
		"AE":listOf(point), #
 
		"AW":listOf(stone), #
 
		"PL":singleton(color),
 
		"C":singleton(text(simple=False)),
 
		"DM":singleton(double),
 
		"GB":singleton(double),
 
		"GW":singleton(double),
 
		"HO":singleton(double),
 
		"N":singleton(text()),
 
		"UC":singleton(double),
 
		"V":singleton(real),
 
		"BM":singleton(double),
 
		"DO":singleton(empty),
 
		"IT":singleton(empty),
 
		"TE":singleton(double),
 
		"AR":listOf(compose(point,point)), #
 
		"CR":listOf(point), #
 
		"DD":listOf(point,allowEmpty=True), #
 
		"LB":listOf(compose(point,text())), #
 
		"LN":listOf(compose(point,point)), #
 
		"MA":listOf(point), #
 
		"SL":listOf(point), #
 
		"SQ":listOf(point), #
 
		"TR":listOf(point), #
 
		"AP":singleton(compose(text(composed=True),text())), #
 
		"CA":singleton(text()),
 
		"FF":singleton(number),
 
		"GM":singleton(number),
 
		"ST":singleton(number),
 
		"SZ":choose(singleton(number),singleton(compose(number,number))), #
 
		"AN":singleton(text()),
 
		"BR":singleton(text()),
 
		"BT":singleton(text()),
 
		"CP":singleton(text()),
 
		"DT":singleton(text()),
 
		"EV":singleton(text()),
 
		"GN":singleton(text()),
 
		"GC":singleton(text(simple=False)),
 
		"ON":singleton(text()),
 
		"OT":singleton(text()),
 
		"PB":singleton(text()),
 
		"PC":singleton(text()),
 
		"PW":singleton(text()),
 
		"RE":singleton(text()),
 
		"RO":singleton(text()),
 
		"RU":singleton(text()),
 
		"SO":singleton(text()),
 
		"TM":singleton(real),
 
		"US":singleton(text()),
 
		"WR":singleton(text()),
 
		"WT":singleton(text()),
 
		"BL":singleton(real),
 
		"OB":singleton(number),
 
		"OW":singleton(number),
 
		"WL":singleton(real),
 
		"FG":choose(singleton(empty),singleton(compose(number,text()))), #
 
		"PM":singleton(number),
 
		"VW":listOf(point,allowEmpty=True), #
 
		
 
		# go specific
 
		"HA":singleton(number),
 
		"KM":singleton(real),
 
		"TB":listOf(point,allowEmpty=True),
 
		"TW":listOf(point,allowEmpty=True)
 
	}
 
 
"""def property(str):
 
  # i=propIdent(str)
 
  # if i<0: return -1
 
  # j=i
 
  # i=propValue(str[i:])
 
  # while i>=0:
 
    # j+=i
 
    # i=propValue(str[i:])
 
  # return j
 
	# i=propIdent(str)
 
	# if i<0: return -1
 
	# j=i
 
	# i=propValue(str[i:])
 
	# while i>=0:
 
		# j+=i
 
		# i=propValue(str[i:])
 
	# return j
 
 
def propIdent(str):
 
  m=re.match(r"[A-Z]+",str)
 
  if m is None: return -1
 
  return m.end()
 
	m=re.match(r"[A-Z]+",str)
 
	if m is None: return -1
 
	return m.end()
 
 
def propValue(str):
 
  i=cValueType(str[1:])
 
  if str[0]=="[" and i>=0 and str[i]=="]": return i+1
 
  else: return -1
 
	i=cValueType(str[1:])
 
	if str[0]=="[" and i>=0 and str[i]=="]": return i+1
 
	else: return -1
 
 
class propValue:
 
  pass
 
	pass
 
 
def cValueType(str,start):
 
  matches=[real,number,double,color,simpleText]
 
  for f in matches:
 
    i,x=f(str,start)
 
    if x is not None: return i,x
 
  return 1/0
 
	matches=[real,number,double,color,simpleText]
 
	for f in matches:
 
		i,x=f(str,start)
 
		if x is not None: return i,x
 
	return 1/0
 
 
def number(str,start):
 
  r=re.compile(r"(\+|-|)\d+")
 
  m=r.match(str,start)
 
  if m is None: return (-1,None)
 
  x=int(m.group(0))
 
  return (m.end(),x)
 
	r=re.compile(r"(\+|-|)\d+")
 
	m=r.match(str,start)
 
	if m is None: return (-1,None)
 
	x=int(m.group(0))
 
	return (m.end(),x)
 
 
def real(str):
 
  m=re.match(r"(\+|-|)\d+(\.\d+)?",str)
 
  if m is None: return -1
 
  return m.end()
 
	m=re.match(r"(\+|-|)\d+(\.\d+)?",str)
 
	if m is None: return -1
 
	return m.end()
 
 
def double(str):
 
  m=re.match(r"1|2",str)
 
  if m is None: return -1
 
  return m.end()
 
  
 
	m=re.match(r"1|2",str)
 
	if m is None: return -1
 
	return m.end()
 
	
 
def color(str):
 
  m=re.match(r"B|W",str)
 
  if m is None: return -1
 
  return m.end()
 
  
 
	m=re.match(r"B|W",str)
 
	if m is None: return -1
 
	return m.end()
 
	
 
def simpleText(str):
 
  res=r""
 
  esc=False
 
  for c in str:
 
    if esc:
 
      res+=c
 
      esc=False
 
    elif c=="\\":
 
      esc=True
 
    elif c=="]":
 
      break
 
    else:
 
      res+=c
 
  return res"""
 
	res=r""
 
	esc=False
 
	for c in str:
 
		if esc:
 
			res+=c
 
			esc=False
 
		elif c=="\\":
 
			esc=True
 
		elif c=="]":
 
			break
 
		else:
 
			res+=c
 
	return res"""
 
 
sgf=open("in/1-Hora-Simara.sgf").read()
 
 
x=Collection(sgf)
 
 
# TODO:
 
# date
 
 
 
"""
 
# move
 
B	move
 
KO	none
 
MN	number
 
W	move
 
 
# setup
 
AB	list of stone
 
AE	list of point
 
AW	list of stone
 
PL	color
 
 
# node annotation
 
C	text
 
DM	double
 
GB	double
 
GW	double
 
HO	double
 
N	simpleText
 
UC	double
 
V	real
 
 
# move annotation
 
BM	double
 
DO	none
 
IT	none
 
TE	double
 
 
# markup
 
AR	list of composed point:point
 
CR	list of point
 
DD	elist of point
 
LB	list of composed point:simpleText
 
LN	list of composed point:point
 
MA	list of point
 
SL	list of point
 
SQ	list of point
 
TR	list of point
 
 
# root
 
AP	composed simpleText:simpleText
 
CA	simpleText
 
FF	number
 
GM	number
 
ST	number
 
SZ	number | composed number:number
 
 
# game info
 
AN	simpleText
 
BR	simpleText
 
BT	simpleText
 
CP	simpleText
 
DT	simpleText
 
EV	simpleText
 
GN	simpleText
 
GC	text
 
ON	simpleText
 
OT	simpleText
 
PB	simpleText
 
PC	simpleText
 
PW	simpleText
 
RE	simpleText
 
RO	simpleText
 
RU	simpleText
 
SO	simpleText
 
TM	real
 
US	simpleText
 
WR	simpleText
 
WT	simpleText
 
 
# timing
 
BL	real
 
OB	number
 
OW	number
 
WL	real
 
 
# misc
 
FG	none | composition of number:simpleText
 
PM	number
 
VW	elist of point
 
"""
 
\ No newline at end of file
sgfParser.py
Show inline comments
 
import re
 
  
 
	
 
def skipWhitespace(str,start):
 
  i=start
 
  while i<len(str) and str[i].isspace(): i+=1
 
  return i
 
	i=start
 
	while i<len(str) and str[i].isspace(): i+=1
 
	return i
 
 
class Collection:
 
  gameTrees=[]
 
  
 
  def __init__(self,str):
 
    self.gameTrees=[]
 
    i,x=GameTree.create(str,0)
 
    if x is None:
 
      print("error when parsing Collection")
 
      return
 
    while x is not None:
 
      self.gameTrees.append(x)
 
      i,x=GameTree.create(str,i)
 
  
 
	gameTrees=[]
 
	
 
	def __init__(self,str):
 
		self.gameTrees=[]
 
		i,x=GameTree.create(str,0)
 
		if x is None:
 
			print("error when parsing Collection")
 
			return
 
		while x is not None:
 
			self.gameTrees.append(x)
 
			i,x=GameTree.create(str,i)
 
	
 
class GameTree:
 
  nodes=[]
 
  branches=[]
 
  
 
  # def __init__(self,str,start):
 
    # self.nodes=[]
 
    # self.branches=[]
 
    # if str[start]!="(":
 
      # print("error when parsing GameTree")
 
      # return (-1,None)
 
    # i,x=Node(str,start+1)
 
    # if x is None:
 
      # print("error when parsing GameTree")
 
      # return (-1,None)
 
    # while x is not None:
 
      # self.nodes.append(x)
 
      # i,x=Node(str,i)
 
    # if str[i]!=")":
 
      # print("error when parsing GameTree")
 
      # return (-1,None)
 
    # return (i+1,self)
 
    
 
  def create(str,start):
 
    res=GameTree()
 
    i=skipWhitespace(str,start)
 
    if i>=len(str) or str[i]!="(":
 
      # print("error when parsing GameTree")
 
      return (start,None)
 
    i,x=Node.create(str,start+1)
 
    if x is None:
 
      # print("error when parsing GameTree")
 
      return (i,None)
 
    while x is not None:
 
      res.nodes.append(x)
 
      i=skipWhitespace(str,i)
 
      i,x=Node.create(str,i)
 
    i=skipWhitespace(str,i)
 
    i,x=GameTree.create(str,i)
 
    while x is not None:
 
      res.branches.append(x)
 
      i=skipWhitespace(str,i)
 
      i,x=GameTree.create(str,i)
 
    if str[i]!=")":
 
      # print("error when parsing GameTree")
 
      return (i,None)
 
    return (i+1,res)
 
  
 
	nodes=[]
 
	branches=[]
 
	
 
	# def __init__(self,str,start):
 
		# self.nodes=[]
 
		# self.branches=[]
 
		# if str[start]!="(":
 
			# print("error when parsing GameTree")
 
			# return (-1,None)
 
		# i,x=Node(str,start+1)
 
		# if x is None:
 
			# print("error when parsing GameTree")
 
			# return (-1,None)
 
		# while x is not None:
 
			# self.nodes.append(x)
 
			# i,x=Node(str,i)
 
		# if str[i]!=")":
 
			# print("error when parsing GameTree")
 
			# return (-1,None)
 
		# return (i+1,self)
 
		
 
	def create(str,start):
 
		res=GameTree()
 
		i=skipWhitespace(str,start)
 
		if i>=len(str) or str[i]!="(":
 
			# print("error when parsing GameTree")
 
			return (start,None)
 
		i,x=Node.create(str,start+1)
 
		if x is None:
 
			# print("error when parsing GameTree")
 
			return (i,None)
 
		while x is not None:
 
			res.nodes.append(x)
 
			i=skipWhitespace(str,i)
 
			i,x=Node.create(str,i)
 
		i=skipWhitespace(str,i)
 
		i,x=GameTree.create(str,i)
 
		while x is not None:
 
			res.branches.append(x)
 
			i=skipWhitespace(str,i)
 
			i,x=GameTree.create(str,i)
 
		if str[i]!=")":
 
			# print("error when parsing GameTree")
 
			return (i,None)
 
		return (i+1,res)
 
	
 
class Node:
 
  properties=dict()
 
  
 
  def create(str,start):
 
    res=Node()
 
    if str[start]!=";":
 
      # print("error when parsing Node")
 
      return (start,None)
 
    i=skipWhitespace(str,start+1)
 
    i,x=Property.create(str,start+1)
 
    while x is not None:
 
      if x.name in res.properties:
 
        print('error: duplicate "{0}" property in node at position {1}. second value ignored'.format(x.name,start))
 
      else:
 
        res.properties[x.name]=x
 
      i=skipWhitespace(str,i)
 
      i,x=Property.create(str,i)
 
    return (i,res)
 
    
 
  def setProperty(self,name,value):
 
    self.properties[name]=value
 
    # zkontrolovat typ value
 
    
 
  def getProperty(self,name):
 
    if name in self.properties: return self.properties[name]
 
    else: return None
 
  
 
	properties=dict()
 
	
 
	def create(str,start):
 
		res=Node()
 
		if str[start]!=";":
 
			# print("error when parsing Node")
 
			return (start,None)
 
		i=skipWhitespace(str,start+1)
 
		i,x=Property.create(str,start+1)
 
		while x is not None:
 
			if x.name in res.properties:
 
				print('error: duplicate "{0}" property in node at position {1}. second value ignored'.format(x.name,start))
 
			else:
 
				res.properties[x.name]=x
 
			i=skipWhitespace(str,i)
 
			i,x=Property.create(str,i)
 
		return (i,res)
 
		
 
	def setProperty(self,name,value):
 
		self.properties[name]=value
 
		# zkontrolovat typ value
 
		
 
	def getProperty(self,name):
 
		if name in self.properties: return self.properties[name]
 
		else: return None
 
	
 
class Property:
 
  name=""
 
  value=""
 
  
 
  def create(str,start):
 
    res=Property()
 
    i,x=Property.ident(str,start)
 
    if x is None:
 
      return (start,None)
 
    res.name=x
 
    i,x=PropValue.create(str,i,res.name)
 
    if x is None:
 
      print('error when parsing property "{0}" at position {1}'.format(res.name,i))
 
      return (start,None)
 
    # while x is not None: # přesunuto do PropValue.listOf
 
      # res.values.append(x)
 
      # i=skipWhitespace(str,i)
 
      # i,x=PropValue.create(str,i,res.name)
 
    return (i,res)
 
    
 
  def ident(str,start):
 
    r=re.compile(r"[A-Z]+")
 
    m=r.match(str,start)
 
    if m is None: return (start,None)
 
    return (m.end(),m.group())
 
	name=""
 
	value=""
 
	
 
	def create(str,start):
 
		res=Property()
 
		i,x=Property.ident(str,start)
 
		if x is None:
 
			return (start,None)
 
		res.name=x
 
		i,x=PropValue.create(str,i,res.name)
 
		if x is None:
 
			print('error when parsing property "{0}" at position {1}'.format(res.name,i))
 
			return (start,None)
 
		# while x is not None: # přesunuto do PropValue.listOf
 
			# res.values.append(x)
 
			# i=skipWhitespace(str,i)
 
			# i,x=PropValue.create(str,i,res.name)
 
		return (i,res)
 
		
 
	def ident(str,start):
 
		r=re.compile(r"[A-Z]+")
 
		m=r.match(str,start)
 
		if m is None: return (start,None)
 
		return (m.end(),m.group())
 
 
class PropValue:
 
  type=""
 
  value=None
 
  patterns=dict()
 
  
 
  def create(str,start,name):
 
    if name in PropValue.patterns:
 
      return PropValue.patterns[name](str,start)
 
    else:
 
      print('warning, unknown property "{0}" at position {1}'.format(name,start))
 
      return PropValue.singleton(PropValue.anything)(str,start)
 
  
 
  # def singleton(str,start,vType):
 
    # if str[start]!="[":
 
      # return (start,None)
 
    # i,x=vType(str,start+1)
 
    # if x is None: return (start,None)
 
    # if str[i]!="]":
 
      # return (start,None)
 
    # return (i+1,x)
 
  
 
  def choose(*vTypes):
 
    def f(str,start):
 
      for vType in vTypes:
 
        i,x=vType(str,start)
 
        if x is not None: return (i,x)
 
      return (start,None)
 
    return f
 
  
 
  def singleton(vType):
 
    def f(str,start):
 
      if str[start]!="[":
 
        return (start,None)
 
      i,x=vType(str,start+1)
 
      if x is None: return (start,None)
 
      if str[i]!="]":
 
        return (start,None)
 
      return (i+1,x)
 
    return f
 
    
 
  # def listOf(str,start,vType,allowEmpty=False):
 
    # res=[]
 
    # i,x=singleton(str,start,vType)
 
    # # singleton(vType) if vType not tuple else compose(vType[0],vType[1])
 
    # while x!=None:
 
      # res.append(x)
 
      # i,x=singleton(str,i,vType)
 
    # if len(res)==0 and not allowEmpty: return (start,None)
 
    # return (i,res)
 
    
 
  def listOf(vType,allowEmpty=False):
 
    def f(str,start):
 
      res=[]
 
      single=singleton(vType)
 
      i,x=single(str,start)
 
      while x!=None:
 
        res.append(x)
 
        i,x=single(str,i)
 
      if len(res)==0 and not allowEmpty: return (start,None)
 
      return (i,res)
 
    return f
 
    
 
  # def compose(str,start,vTypeA,vTypeB):
 
    # i,a=vTypeA(str,start)
 
    # if a==None or str[i]!=":": return (start,None)
 
    # i,b=vTypeB(str,i+1)
 
    # if b==None: return start,None
 
    # return (i,(a,b))
 
    
 
  def compose(vTypeA,vTypeB):
 
    def f(str,start):
 
      i,a=vTypeA(str,start)
 
      # print(">",i,a)
 
      if a==None or str[i]!=":": return (start,None)
 
      i,b=vTypeB(str,i+1)
 
      # print(">",i,b)
 
      if b==None: return start,None
 
      return (i,(a,b))
 
    return f
 
  
 
  def number(str,start):
 
    r=re.compile(r"(\+|-|)\d+")
 
    m=r.match(str,start)
 
    if m is None: return (start,None)
 
    res=int(m.group(0))
 
    return (m.end(),res)
 
    
 
  def real(str,start):
 
    r=re.compile(r"(\+|-|)\d+(\.\d+)?")
 
    m=r.match(str,start)
 
    if m is None: return (start,None)
 
    res=float(m.group(0))
 
    return (m.end(),res)
 
    
 
  def double(str,start):
 
    r=re.compile(r"1|2")
 
    m=r.match(str,start)
 
    if m is None: return (start,None)
 
    res=int(m.group(0))
 
    return (m.end(),res)
 
  
 
  def color(str,start):
 
    r=re.compile(r"B|W")
 
    m=r.match(str,start)
 
    if m is None: return (start,None)
 
    return (m.end(),m.group(0))
 
    
 
  # def simpleText(str,start):
 
    # res=""
 
    # esc=False
 
    # lineBreak=False
 
    # for c in str:
 
      # if esc:
 
        # res+=c
 
        # esc=False
 
      # elif c=="\\":
 
        # esc=True
 
      # elif c=="]":
 
        # break
 
      # else:
 
        # res+=c
 
    # return res
 
  
 
  def text(simple=True,composed=False):
 
    def f(str,start):
 
      res=""
 
      esc=False
 
      lastC=""
 
      for i,c in enumerate(str[start:],start):
 
        if esc:
 
          if c!="\n" and c!="\r": res+=c
 
          esc=False
 
        elif (c=="\n" and lastC=="\r") or (c=="\r" and lastC=="\n"): pass
 
        elif c=="\r" or c=="\n" and not simple:
 
          res+="\n"
 
        elif c.isspace():
 
          res+=" "
 
        elif c=="\\":
 
          esc=True
 
        elif c=="]" or (c==":" and composed):
 
          break
 
        else:
 
          res+=c
 
        lastC=c
 
      return (i,res)
 
    return f
 
    
 
  def empty(str,start): return (start,"")
 
  
 
  def anything(str,start):
 
    esc=False
 
    for i,c in enumerate(str[start:],start):
 
      if esc: esc=False
 
      elif c=="\\": esc=True
 
      elif c=="]": break
 
    return (i,str[start:i])
 
  
 
  # go specific
 
  def point(str,start):
 
    r=re.compile(r"[a-zA-Z]{2}|") # !! limit to board size
 
    m=r.match(str,start)
 
    if m is None: return (start,None)
 
    if m.group(0)=="": # pass, !! tt
 
      return (m.end(),tuple())
 
    col=m.group(0)[0]
 
    row=m.group(0)[1]
 
    col=ord(col) - (ord("a") if "a"<=col<="z" else ord("A")-26)
 
    row=ord(row) - (ord("a") if "a"<=row<="z" else ord("A")-26)
 
    return (m.end(),(col,row))
 
	type=""
 
	value=None
 
	patterns=dict()
 
	
 
	def create(str,start,name):
 
		if name in PropValue.patterns:
 
			return PropValue.patterns[name](str,start)
 
		else:
 
			print('warning, unknown property "{0}" at position {1}'.format(name,start))
 
			return PropValue.singleton(PropValue.anything)(str,start)
 
	
 
	# def singleton(str,start,vType):
 
		# if str[start]!="[":
 
			# return (start,None)
 
		# i,x=vType(str,start+1)
 
		# if x is None: return (start,None)
 
		# if str[i]!="]":
 
			# return (start,None)
 
		# return (i+1,x)
 
	
 
	def choose(*vTypes):
 
		def f(str,start):
 
			for vType in vTypes:
 
				i,x=vType(str,start)
 
				if x is not None: return (i,x)
 
			return (start,None)
 
		return f
 
	
 
	def singleton(vType):
 
		def f(str,start):
 
			if str[start]!="[":
 
				return (start,None)
 
			i,x=vType(str,start+1)
 
			if x is None: return (start,None)
 
			if str[i]!="]":
 
				return (start,None)
 
			return (i+1,x)
 
		return f
 
		
 
	# def listOf(str,start,vType,allowEmpty=False):
 
		# res=[]
 
		# i,x=singleton(str,start,vType)
 
		# # singleton(vType) if vType not tuple else compose(vType[0],vType[1])
 
		# while x!=None:
 
			# res.append(x)
 
			# i,x=singleton(str,i,vType)
 
		# if len(res)==0 and not allowEmpty: return (start,None)
 
		# return (i,res)
 
		
 
	def listOf(vType,allowEmpty=False):
 
		def f(str,start):
 
			res=[]
 
			single=singleton(vType)
 
			i,x=single(str,start)
 
			while x!=None:
 
				res.append(x)
 
				i,x=single(str,i)
 
			if len(res)==0 and not allowEmpty: return (start,None)
 
			return (i,res)
 
		return f
 
		
 
	# def compose(str,start,vTypeA,vTypeB):
 
		# i,a=vTypeA(str,start)
 
		# if a==None or str[i]!=":": return (start,None)
 
		# i,b=vTypeB(str,i+1)
 
		# if b==None: return start,None
 
		# return (i,(a,b))
 
		
 
	def compose(vTypeA,vTypeB):
 
		def f(str,start):
 
			i,a=vTypeA(str,start)
 
			# print(">",i,a)
 
			if a==None or str[i]!=":": return (start,None)
 
			i,b=vTypeB(str,i+1)
 
			# print(">",i,b)
 
			if b==None: return start,None
 
			return (i,(a,b))
 
		return f
 
	
 
	def number(str,start):
 
		r=re.compile(r"(\+|-|)\d+")
 
		m=r.match(str,start)
 
		if m is None: return (start,None)
 
		res=int(m.group(0))
 
		return (m.end(),res)
 
		
 
	def real(str,start):
 
		r=re.compile(r"(\+|-|)\d+(\.\d+)?")
 
		m=r.match(str,start)
 
		if m is None: return (start,None)
 
		res=float(m.group(0))
 
		return (m.end(),res)
 
		
 
	def double(str,start):
 
		r=re.compile(r"1|2")
 
		m=r.match(str,start)
 
		if m is None: return (start,None)
 
		res=int(m.group(0))
 
		return (m.end(),res)
 
	
 
	def color(str,start):
 
		r=re.compile(r"B|W")
 
		m=r.match(str,start)
 
		if m is None: return (start,None)
 
		return (m.end(),m.group(0))
 
		
 
	# def simpleText(str,start):
 
		# res=""
 
		# esc=False
 
		# lineBreak=False
 
		# for c in str:
 
			# if esc:
 
				# res+=c
 
				# esc=False
 
			# elif c=="\\":
 
				# esc=True
 
			# elif c=="]":
 
				# break
 
			# else:
 
				# res+=c
 
		# return res
 
	
 
	def text(simple=True,composed=False):
 
		def f(str,start):
 
			res=""
 
			esc=False
 
			lastC=""
 
			for i,c in enumerate(str[start:],start):
 
				if esc:
 
					if c!="\n" and c!="\r": res+=c
 
					esc=False
 
				elif (c=="\n" and lastC=="\r") or (c=="\r" and lastC=="\n"): pass
 
				elif c=="\r" or c=="\n" and not simple:
 
					res+="\n"
 
				elif c.isspace():
 
					res+=" "
 
				elif c=="\\":
 
					esc=True
 
				elif c=="]" or (c==":" and composed):
 
					break
 
				else:
 
					res+=c
 
				lastC=c
 
			return (i,res)
 
		return f
 
		
 
	def empty(str,start): return (start,"")
 
	
 
	def anything(str,start):
 
		esc=False
 
		for i,c in enumerate(str[start:],start):
 
			if esc: esc=False
 
			elif c=="\\": esc=True
 
			elif c=="]": break
 
		return (i,str[start:i])
 
	
 
	# go specific
 
	def point(str,start):
 
		r=re.compile(r"[a-zA-Z]{2}|") # !! limit to board size
 
		m=r.match(str,start)
 
		if m is None: return (start,None)
 
		if m.group(0)=="": # pass, !! tt
 
			return (m.end(),tuple())
 
		col=m.group(0)[0]
 
		row=m.group(0)[1]
 
		col=ord(col) - (ord("a") if "a"<=col<="z" else ord("A")-26)
 
		row=ord(row) - (ord("a") if "a"<=row<="z" else ord("A")-26)
 
		return (m.end(),(col,row))
 
 
  move=point
 
  stone=point
 
  
 
  patterns={
 
    "B":singleton(move),
 
    "KO":singleton(empty),
 
    "MN":singleton(number),
 
    "W":singleton(move),
 
    "AB":listOf(stone), #
 
    "AE":listOf(point), #
 
    "AW":listOf(stone), #
 
    "PL":singleton(color),
 
    "C":singleton(text(simple=False)),
 
    "DM":singleton(double),
 
    "GB":singleton(double),
 
    "GW":singleton(double),
 
    "HO":singleton(double),
 
    "N":singleton(text()),
 
    "UC":singleton(double),
 
    "V":singleton(real),
 
    "BM":singleton(double),
 
    "DO":singleton(empty),
 
    "IT":singleton(empty),
 
    "TE":singleton(double),
 
    "AR":listOf(compose(point,point)), #
 
    "CR":listOf(point), #
 
    "DD":listOf(point,allowEmpty=True), #
 
    "LB":listOf(compose(point,text())), #
 
    "LN":listOf(compose(point,point)), #
 
    "MA":listOf(point), #
 
    "SL":listOf(point), #
 
    "SQ":listOf(point), #
 
    "TR":listOf(point), #
 
    "AP":singleton(compose(text(composed=True),text())), #
 
    "CA":singleton(text()),
 
    "FF":singleton(number),
 
    "GM":singleton(number),
 
    "ST":singleton(number),
 
    "SZ":choose(singleton(number),singleton(compose(number,number))), #
 
    "AN":singleton(text()),
 
    "BR":singleton(text()),
 
    "BT":singleton(text()),
 
    "CP":singleton(text()),
 
    "DT":singleton(text()),
 
    "EV":singleton(text()),
 
    "GN":singleton(text()),
 
    "GC":singleton(text(simple=False)),
 
    "ON":singleton(text()),
 
    "OT":singleton(text()),
 
    "PB":singleton(text()),
 
    "PC":singleton(text()),
 
    "PW":singleton(text()),
 
    "RE":singleton(text()),
 
    "RO":singleton(text()),
 
    "RU":singleton(text()),
 
    "SO":singleton(text()),
 
    "TM":singleton(real),
 
    "US":singleton(text()),
 
    "WR":singleton(text()),
 
    "WT":singleton(text()),
 
    "BL":singleton(real),
 
    "OB":singleton(number),
 
    "OW":singleton(number),
 
    "WL":singleton(real),
 
    "FG":choose(singleton(empty),singleton(compose(number,text()))), #
 
    "PM":singleton(number),
 
    "VW":listOf(point,allowEmpty=True), #
 
    
 
    # go specific
 
    "HA":singleton(number),
 
    "KM":singleton(real),
 
    "TB":listOf(point,allowEmpty=True),
 
    "TW":listOf(point,allowEmpty=True)
 
  }
 
	move=point
 
	stone=point
 
	
 
	patterns={
 
		"B":singleton(move),
 
		"KO":singleton(empty),
 
		"MN":singleton(number),
 
		"W":singleton(move),
 
		"AB":listOf(stone), #
 
		"AE":listOf(point), #
 
		"AW":listOf(stone), #
 
		"PL":singleton(color),
 
		"C":singleton(text(simple=False)),
 
		"DM":singleton(double),
 
		"GB":singleton(double),
 
		"GW":singleton(double),
 
		"HO":singleton(double),
 
		"N":singleton(text()),
 
		"UC":singleton(double),
 
		"V":singleton(real),
 
		"BM":singleton(double),
 
		"DO":singleton(empty),
 
		"IT":singleton(empty),
 
		"TE":singleton(double),
 
		"AR":listOf(compose(point,point)), #
 
		"CR":listOf(point), #
 
		"DD":listOf(point,allowEmpty=True), #
 
		"LB":listOf(compose(point,text())), #
 
		"LN":listOf(compose(point,point)), #
 
		"MA":listOf(point), #
 
		"SL":listOf(point), #
 
		"SQ":listOf(point), #
 
		"TR":listOf(point), #
 
		"AP":singleton(compose(text(composed=True),text())), #
 
		"CA":singleton(text()),
 
		"FF":singleton(number),
 
		"GM":singleton(number),
 
		"ST":singleton(number),
 
		"SZ":choose(singleton(number),singleton(compose(number,number))), #
 
		"AN":singleton(text()),
 
		"BR":singleton(text()),
 
		"BT":singleton(text()),
 
		"CP":singleton(text()),
 
		"DT":singleton(text()),
 
		"EV":singleton(text()),
 
		"GN":singleton(text()),
 
		"GC":singleton(text(simple=False)),
 
		"ON":singleton(text()),
 
		"OT":singleton(text()),
 
		"PB":singleton(text()),
 
		"PC":singleton(text()),
 
		"PW":singleton(text()),
 
		"RE":singleton(text()),
 
		"RO":singleton(text()),
 
		"RU":singleton(text()),
 
		"SO":singleton(text()),
 
		"TM":singleton(real),
 
		"US":singleton(text()),
 
		"WR":singleton(text()),
 
		"WT":singleton(text()),
 
		"BL":singleton(real),
 
		"OB":singleton(number),
 
		"OW":singleton(number),
 
		"WL":singleton(real),
 
		"FG":choose(singleton(empty),singleton(compose(number,text()))), #
 
		"PM":singleton(number),
 
		"VW":listOf(point,allowEmpty=True), #
 
		
 
		# go specific
 
		"HA":singleton(number),
 
		"KM":singleton(real),
 
		"TB":listOf(point,allowEmpty=True),
 
		"TW":listOf(point,allowEmpty=True)
 
	}
 
 
"""def property(str):
 
  # i=propIdent(str)
 
  # if i<0: return -1
 
  # j=i
 
  # i=propValue(str[i:])
 
  # while i>=0:
 
    # j+=i
 
    # i=propValue(str[i:])
 
  # return j
 
	# i=propIdent(str)
 
	# if i<0: return -1
 
	# j=i
 
	# i=propValue(str[i:])
 
	# while i>=0:
 
		# j+=i
 
		# i=propValue(str[i:])
 
	# return j
 
 
def propIdent(str):
 
  m=re.match(r"[A-Z]+",str)
 
  if m is None: return -1
 
  return m.end()
 
	m=re.match(r"[A-Z]+",str)
 
	if m is None: return -1
 
	return m.end()
 
 
def propValue(str):
 
  i=cValueType(str[1:])
 
  if str[0]=="[" and i>=0 and str[i]=="]": return i+1
 
  else: return -1
 
	i=cValueType(str[1:])
 
	if str[0]=="[" and i>=0 and str[i]=="]": return i+1
 
	else: return -1
 
 
class propValue:
 
  pass
 
	pass
 
 
def cValueType(str,start):
 
  matches=[real,number,double,color,simpleText]
 
  for f in matches:
 
    i,x=f(str,start)
 
    if x is not None: return i,x
 
  return 1/0
 
	matches=[real,number,double,color,simpleText]
 
	for f in matches:
 
		i,x=f(str,start)
 
		if x is not None: return i,x
 
	return 1/0
 
 
def number(str,start):
 
  r=re.compile(r"(\+|-|)\d+")
 
  m=r.match(str,start)
 
  if m is None: return (-1,None)
 
  x=int(m.group(0))
 
  return (m.end(),x)
 
	r=re.compile(r"(\+|-|)\d+")
 
	m=r.match(str,start)
 
	if m is None: return (-1,None)
 
	x=int(m.group(0))
 
	return (m.end(),x)
 
 
def real(str):
 
  m=re.match(r"(\+|-|)\d+(\.\d+)?",str)
 
  if m is None: return -1
 
  return m.end()
 
	m=re.match(r"(\+|-|)\d+(\.\d+)?",str)
 
	if m is None: return -1
 
	return m.end()
 
 
def double(str):
 
  m=re.match(r"1|2",str)
 
  if m is None: return -1
 
  return m.end()
 
  
 
	m=re.match(r"1|2",str)
 
	if m is None: return -1
 
	return m.end()
 
	
 
def color(str):
 
  m=re.match(r"B|W",str)
 
  if m is None: return -1
 
  return m.end()
 
  
 
	m=re.match(r"B|W",str)
 
	if m is None: return -1
 
	return m.end()
 
	
 
def simpleText(str):
 
  res=r""
 
  esc=False
 
  for c in str:
 
    if esc:
 
      res+=c
 
      esc=False
 
    elif c=="\\":
 
      esc=True
 
    elif c=="]":
 
      break
 
    else:
 
      res+=c
 
  return res"""
 
	res=r""
 
	esc=False
 
	for c in str:
 
		if esc:
 
			res+=c
 
			esc=False
 
		elif c=="\\":
 
			esc=True
 
		elif c=="]":
 
			break
 
		else:
 
			res+=c
 
	return res"""
 
 
sgf=open("in/1-Hora-Simara.sgf").read()
 
 
x=Collection(sgf)
 
 
# TODO:
 
# date
 
 
 
"""
 
# move
 
B	move
 
KO	none
 
MN	number
 
W	move
 
 
# setup
 
AB	list of stone
 
AE	list of point
 
AW	list of stone
 
PL	color
 
 
# node annotation
 
C	text
 
DM	double
 
GB	double
 
GW	double
 
HO	double
 
N	simpleText
 
UC	double
 
V	real
 
 
# move annotation
 
BM	double
 
DO	none
 
IT	none
 
TE	double
 
 
# markup
 
AR	list of composed point:point
 
CR	list of point
 
DD	elist of point
 
LB	list of composed point:simpleText
 
LN	list of composed point:point
 
MA	list of point
 
SL	list of point
 
SQ	list of point
 
TR	list of point
 
 
# root
 
AP	composed simpleText:simpleText
 
CA	simpleText
 
FF	number
 
GM	number
 
ST	number
 
SZ	number | composed number:number
 
 
# game info
 
AN	simpleText
 
BR	simpleText
 
BT	simpleText
 
CP	simpleText
 
DT	simpleText
 
EV	simpleText
 
GN	simpleText
 
GC	text
 
ON	simpleText
 
OT	simpleText
 
PB	simpleText
 
PC	simpleText
 
PW	simpleText
 
RE	simpleText
 
RO	simpleText
 
RU	simpleText
 
SO	simpleText
 
TM	real
 
US	simpleText
 
WR	simpleText
 
WT	simpleText
 
 
# timing
 
BL	real
 
OB	number
 
OW	number
 
WL	real
 
 
# misc
 
FG	none | composition of number:simpleText
 
PM	number
 
VW	elist of point
 
"""
 
\ No newline at end of file
0 comments (0 inline, 0 general)