Files
@ 966ee650fabf
Branch filter:
Location: Diana/src/diana/diana.py
966ee650fabf
4.4 KiB
text/x-python
updated comments
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 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | import os
import re
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 collect_moves(root):
"""Walk the primary variation and collect the move coordinates.
:param Node root: a node serving as a root for the tree traversal
:return: sequence of move colors and coordinates (col, row)
:rtype: Iterator[("b" or "w", (int, int))]"""
node = root
while len(node.children) > 0:
b = node.get_prop("B")
w = node.get_prop("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, file_name):
self.file_name = file_name
self._short_name = "".join(re.split(r'[/\\]', file_name)[-1].split('.')[:-1])
self._game = go.Go()
with open(self.file_name, 'r', encoding=cfg.encoding) as f:
games = Collection(f.read()).list_games()
self._record = list(games)[0]
self._moves = list(collect_moves(self._record.root))
def process(self):
print("{0}... ".format(self.file_name), end="")
i = 1
for k in range(0, len(self._moves), cfg.moves_per_diagram):
filename = os.path.join(cfg.output_dir, "{0}-{1}".format(self._short_name, i))
self.create_diagram(k, k + cfg.moves_per_diagram).save(filename, "templ-pleb.svg")
i += 1
info_str = """{GN}
B: {PB} {BR}
W: {PW} {WR}
{DT}
{RE}""".format(**self.fetch_game_info(["GN", "PB", "BR", "PW", "WR", "DT", "RE"], ""))
notes = open(os.path.join(cfg.output_dir, "{0}.txt".format(self._short_name)), 'w')
notes.write(info_str)
notes.close()
print("done")
def create_diagram(self, start, end):
"""Create and return a diagram drawer instance.
:param int start: the initial move
:param int end: the first omitted move, similar to sequence slices
:return: a diagram instance
:rtype: Drawer"""
# initialize the diagram
template = Svg()
self._set_move(start)
# draw current state
for (line_number, line) in enumerate(self._game.board):
for (item_number, item) in enumerate(line):
if item != EMPTY:
template.add_stone(item_number, line_number, "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.keep_broken:
continue
else:
return False
# draw the move
template.add_move(c, r, color, k + 1)
return template
def fetch_game_info(self, field_names, default=None):
return {k: self._record.get(k, default) for k in field_names}
def _set_move(self, k):
"""Rewind the internal game state to move k.
:param int k: the move number"""
self._game = go.Go()
black_stones = self._record.root.get_prop("AB")
white_stones = self._record.root.get_prop("AW")
if black_stones:
for p in black_stones:
self._game.board[p.r][p.c] = BLACK
if white_stones:
for p in white_stones:
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):
"""Make a single move.
:param str color: "b" or "w"
:param int c: column
:param int r: row
:return: True if we can continue with another move, False on abort"""
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
# we accept and process only legal moves
msg = "illegal move: {0} at {1},{2}".format(self._game.move_count+1, c, r)
if cfg.keep_broken:
print(msg)
else:
msg += ". aborted"
print(msg)
return False
return True
def main():
cfg.parse_args()
print("processing:")
files = cfg.input_files[:]
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))
|