-
Notifications
You must be signed in to change notification settings - Fork 3
/
mod_pgn_subtree.py
130 lines (101 loc) · 4.4 KB
/
mod_pgn_subtree.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import chess.pgn
import sys
import re
from io import StringIO
from collections import OrderedDict
def insert_braces(text):
# Find all the occurrances of the braced string using re.finditer
brace_matches = list(re.finditer(r'\{(.*?)\}', text, flags=re.DOTALL))
# Iterate through all the matches (in reversed order because inserting stuff messes up the matches completely)
for brace_match in reversed(brace_matches):
start_pos = brace_match.start()
end_pos = brace_match.end()
# Find the position of the first [% within the braces
percent_match = re.search(r'\[%', brace_match.group(1), flags=re.DOTALL)
if percent_match is None:
continue
percent_pos = percent_match.start() + start_pos + 1
comment_text = text[start_pos + 1:percent_pos].strip()
# Insert "} {" at the position of the [% , if and only if the text before is non-empty
if comment_text:
text = text[:percent_pos] + "} { " + text[percent_pos:]
return text
def get_path(node):
path = []
path.append(node.san())
cur_node = node.parent
while cur_node.move:
path.append(cur_node.san())
cur_node = cur_node.parent
nodes_ordered = reversed(path)
return " ".join(nodes_ordered)
# Convert "1.d4 Nf6 2.c4 e6" to "d4 Nf6 c4 e6"
# def strip_leading_move_number(move):
# res = re.findall('^\s*\d*\.{0,3}\s*(\S*)\s*', move)
# if res and res[0]:
# return res[0]
# else:
# return move
# Convert "1.d4 Nf6 2.c4 e6" to "d4 Nf6 c4 e6"
def normalize_and_strip_move_numbers(moves_str):
res = re.sub('\d+\.{1,3}', '', moves_str) # remove move numbers
res = re.sub('\s{2,}', ' ', res) # replace multiple whitespaces with one
res = res.strip()
return res
def strip_leading_move(text):
regex = re.compile(r'\d+\.+\s*\S+\s*')
match = regex.search(text)
while (match):
return text[match.end():]
def pgn_subtree(filename, moves_str):
pgn_stream = open(filename, encoding="utf-8")
return get_pgn_subtree(pgn_stream, moves_str)
def pgn_subtree_from_string(pgn_str, moves_str):
pgn_stream = StringIO(pgn_str)
sys.tracebacklimit = -1
return get_pgn_subtree(pgn_stream, moves_str)
def get_pgn_subtree(pgn, moves_str):
#moves = list(filter(None, moves_str.split(' ')))
#print(f"Looking for moves: {moves}")
#moves = map(lambda move: strip_leading_move_number(move), moves)
#path = " ".join(moves)
#print(f" Without move numbers: {path}")
path = normalize_and_strip_move_numbers(moves_str)
#print(f" Without move numbers: {path}")
master_node = chess.pgn.Game()
game = chess.pgn.read_game(pgn)
mlist = []
mlist.extend(game.variations)
variations = [(master_node, mlist)]
done = False
while not done:
newvars = []
done = True
for vnode, nodes in variations:
newmoves = {} # Maps move to its index in newvars.
for node in nodes:
#print(f"Move: {node.san()} path: {get_path(node)} comparing-to: {path}")
if get_path(node) == path:
subtree = str(node)
# Workaround to make sure the annotations end up in their own curly brackets,
# ie 1.e4 { A comment } { [%cal Ge2e4] } instead of 1.e4 { A comment [%cal Ge2e4] }
pgn = insert_braces(subtree)
pgn = strip_leading_move(pgn)
return pgn
if node.move is None:
continue
elif node.move not in list(newmoves):
nvnode = vnode.add_variation(node.move, nags = node.nags)
if len(node.variations) > 0:
done = False
newvars.append((nvnode, node.variations))
newmoves[node.move] = len(newvars) - 1
else:
nvnode, nlist = newvars[newmoves[node.move]]
nvnode.nags.update(node.nags)
if len(node.variations) > 0:
done = False
nlist.extend(node.variations)
newvars[newmoves[node.move]] = (nvnode, nlist)
variations = newvars
return ""