Skip to content

Commit

Permalink
Draft load_game
Browse files Browse the repository at this point in the history
  • Loading branch information
bcorfman committed Nov 1, 2022
1 parent 2ac6236 commit 23de996
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 57 deletions.
2 changes: 1 addition & 1 deletion base/move.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class Move(object):
def __init__(self, squares, annotation=''):
def __init__(self, squares, annotation=""):
self.affected_squares = squares
self.annotation = annotation

Expand Down
5 changes: 3 additions & 2 deletions game/gamemanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ def load_game(self, filepath):
else:
game = reader.read_game(0)
if game is not None:
sg = SavedGame()
self.model.curr_state.clear()
self.model.curr_state.to_move = game.next_to_move
self.num_players = 2
Expand All @@ -130,10 +131,10 @@ def load_game(self, filepath):
for i in game.white_kings:
squares[square_map[i]] = WHITE | KING
self.model.curr_state.reset_undo()
self.model.curr_state.redo_list = game.moves
self.model.curr_state.redo_list = sg.moves
self.model.curr_state.update_piece_count()
self.view.reset_view(self.model)
# self.view.serializer.restore(saved_game.description)
self.view.serializer.restore(game.description)
self.view.curr_annotation = self.view.get_annotation()
self.view.flip_view(game.board_orientation == "white_on_top")
self.view.update_statusbar()
Expand Down
63 changes: 32 additions & 31 deletions gui/boardview.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
keymap, reverse_dict
from gui.autoscrollbar import AutoScrollbar
from gui.hyperlinkmgr import HyperlinkManager
from parsing.textserialize import Serializer
from gui.tooltip import ToolTip


Expand Down Expand Up @@ -116,6 +117,7 @@ def __init__(self, root, **props):
'bullet': self.bullets, 'number': self.numbers,
'hyper': self.addLink}
self.link_manager = HyperlinkManager(self.txt, self._gameMgr.load_game)
self.serializer = Serializer(self.txt, self.link_manager)
self.curr_annotation = ''
self._setup_board(root)
starting_squares = [i for i in self._model.curr_state.valid_squares
Expand Down Expand Up @@ -144,8 +146,8 @@ def _toggle_state(self, tags, btn):
if not already_tagged:
self.txt.tag_add(tag, 'sel.first', 'sel.last')
btn.configure(relief='sunken')
other_btns = self.button_set.difference([btn])
for b in other_btns:
other_buttons = self.button_set.difference([btn])
for b in other_buttons:
b.configure(relief='raised')
else:
btn.configure(relief='raised')
Expand All @@ -171,20 +173,20 @@ def _on_numbers(self):
def _process_button_click(self, tag, tooltip, add_func, remove_func):
tooltip.hide()
if self.txt.tag_ranges('sel'):
startline, _ = parse_index(self.txt.index('sel.first'))
endline, _ = parse_index(self.txt.index('sel.last'))
start_line, _ = parse_index(self.txt.index('sel.first'))
end_line, _ = parse_index(self.txt.index('sel.last'))
else:
startline, _ = parse_index(self.txt.index(INSERT))
endline = startline
current_tags = self.txt.tag_names('%d.0' % startline)
start_line, _ = parse_index(self.txt.index(INSERT))
end_line = start_line
current_tags = self.txt.tag_names('%d.0' % start_line)
if tag not in current_tags:
add_func(startline, endline)
add_func(start_line, end_line)
else:
remove_func(startline, endline)
remove_func(start_line, end_line)

def _add_bullets_if_needed(self, startline, endline):
self._remove_numbers_if_needed(startline, endline)
for line in range(startline, endline+1):
def _add_bullets_if_needed(self, start_line, end_line):
self._remove_numbers_if_needed(start_line, end_line)
for line in range(start_line, end_line + 1):
current_tags = self.txt.tag_names('%d.0' % line)
if 'bullet' not in current_tags:
start = '%d.0' % line
Expand All @@ -196,8 +198,8 @@ def _add_bullets_if_needed(self, startline, endline):
self.bullets.configure(relief='sunken')
self.numbers.configure(relief='raised')

def _remove_bullets_if_needed(self, startline, endline):
for line in range(startline, endline+1):
def _remove_bullets_if_needed(self, start_line, end_line):
for line in range(start_line, end_line + 1):
current_tags = self.txt.tag_names('%d.0' % line)
if 'bullet' in current_tags:
start = '%d.0' % line
Expand All @@ -208,26 +210,26 @@ def _remove_bullets_if_needed(self, startline, endline):
self.txt.delete(start, end)
self.bullets.configure(relief='raised')

def _add_numbers_if_needed(self, startline, endline):
self._remove_bullets_if_needed(startline, endline)
def _add_numbers_if_needed(self, start_line, end_line):
self._remove_bullets_if_needed(start_line, end_line)
num = 1
for line in range(startline, endline+1):
for line in range(start_line, end_line + 1):
current_tags = self.txt.tag_names('%d.0' % line)
if 'number' not in current_tags:
start = '%d.0' % line
end = '%d.end' % line
self.txt.insert(start, '\t')
numstr = '%d.' % num
self.txt.insert(start, numstr)
num_str = '%d.' % num
self.txt.insert(start, num_str)
self.txt.insert(start, '\t')
self.txt.tag_add('number', start, end)
num += 1
self.numbers.configure(relief='sunken')
self.bullets.configure(relief='raised')

def _remove_numbers_if_needed(self, startline, endline):
def _remove_numbers_if_needed(self, start_line, end_line):
cnt = IntVar()
for line in range(startline, endline+1):
for line in range(start_line, end_line + 1):
current_tags = self.txt.tag_names('%d.0' % line)
if 'number' in current_tags:
start = '%d.0' % line
Expand Down Expand Up @@ -344,8 +346,8 @@ def calc_grid_pos(self, pos):

def highlight_square(self, idx, color):
row, col = self._grid_pos[idx]
hpos = col + row * 8
self.canvas.itemconfigure('o'+str(hpos), outline=color)
h_pos = col + row * 8
self.canvas.itemconfigure('o' + str(h_pos), outline=color)

def calc_valid_xy(self, x, y):
return (min(max(0, self.canvas.canvasx(x)), self._board_side-1),
Expand All @@ -354,15 +356,15 @@ def calc_valid_xy(self, x, y):
def notify(self, move):
add_lst = []
rem_lst = []
for idx, _, newval in move.affected_squares:
if newval & FREE:
for idx, _, new_val in move.affected_squares:
if new_val & FREE:
rem_lst.append(idx)
else:
add_lst.append(idx)
cmd = Command(add=add_lst, remove=rem_lst)
self._draw_checkers(cmd)
self.txt.delete('1.0', END)
# self.serializer.restore(move.annotation)
self.serializer.restore(move.annotation)
self.curr_annotation = move.annotation
if self.txt.get('1.0', 'end').strip() == '':
start = keymap[move.affected_squares[FIRST][0]]
Expand All @@ -371,8 +373,7 @@ def notify(self, move):
self.txt.insert('1.0', move_str)

def get_annotation(self):
# return self.serializer.dump()
return ""
return self.serializer.dump()

def erase_checker(self, index):
self.canvas.delete('c'+str(index))
Expand Down Expand Up @@ -455,9 +456,9 @@ def _setup_board(self, _):
def _label_board(self):
for key, pair in self._grid_pos.items():
row, col = pair
xpos, ypos = col * self._square_size, row * self._square_size
self.canvas.create_text(xpos+self._square_size-7,
ypos+self._square_size-7,
x_pos, y_pos = col * self._square_size, row * self._square_size
self.canvas.create_text(x_pos + self._square_size - 7,
y_pos + self._square_size - 7,
text=str(keymap[key]),
fill=LIGHT_SQUARES, tag='label')

Expand Down
8 changes: 5 additions & 3 deletions parsing/PDN.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import charset_normalizer
import os
import textwrap
from parsing.gamepersist import SavedGame
from io import StringIO
from pyparsing import Combine, Forward, Group, LineStart, LineEnd, Literal, OneOrMore, Optional, \
QuotedString, Suppress, Word, WordEnd, WordStart, nums, one_of, rest_of_line, srange
Expand Down Expand Up @@ -142,7 +143,7 @@ def _read_result(self, value):

def _read_board_orientation(self, value):
if value == "white_on_top" or value == "black_on_top":
self._flip_board = "white_on_top" == True
self._flip_board = value == "white_on_top"
else:
raise SyntaxError(f"Unknown {value} used in board_orientation tag.")

Expand Down Expand Up @@ -244,7 +245,7 @@ def read_game(self, idx):
for item in game.body:
if len(item) > 1:
idx = 1
move_list = [list(item[idx])]
move_list = list(item[idx])
if item.comment1:
idx += 1
annotation = item[idx]
Expand All @@ -261,10 +262,11 @@ def read_game(self, idx):
self._moves.append([move_list, annotation])
else:
raise RuntimeError(f"Cannot interpret item {item} in game.body")
sg = SavedGame()
return Game(self._event, self._site, self._date, self._round, self._black_player,
self._white_player, self._next_to_move, self._black_men, self._white_men,
self._black_kings, self._white_kings, self._result, self._flip_board,
self._description, self._moves)
self._description, sg.translate_moves_to_board(self._moves))

def _get_player_to_move(self, turn):
turn = turn.upper()
Expand Down
35 changes: 15 additions & 20 deletions parsing/gamepersist.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import copy
from util.globalconst import FIRST, KING_IDX, LAST, square_map
from util.globalconst import square_map
from game.checkers import Checkers, Checkerboard
from base.move import Move


class SavedGame(object):
Expand All @@ -22,16 +21,18 @@ def __init__(self):
self._wk_check = False

def _is_move(self, delta):
return delta in KING_IDX
return delta in [4, 5]

def _try_move(self, squares: list, annotation: str, state_copy: Checkerboard):
legal_moves = self._model.legal_moves(state_copy)
# try to match squares with available moves on checkerboard
sq_len = len(squares)
for move in legal_moves:
if all(sq == move.affected_squares[i] for i, sq in enumerate(squares)):
if sq_len == len(move.affected_squares) and \
all(square_map[sq] == move.affected_squares[i][0] for i, sq in enumerate(squares)):
move.annotation = annotation
self._model.make_move(move, state_copy, False, False)
self.moves.append(move) #TODO: why do we need this in self.moves instead of returning it?
self.moves.append(move)
return True
else:
raise RuntimeError(f"Illegal move {squares} found")
Expand All @@ -40,23 +41,17 @@ def _try_jump(self, squares: list, annotation: str, state_copy: Checkerboard):
if not self._model.captures_available(state_copy):
return False
legal_moves = self._model.legal_moves(state_copy)
# PDN move follows format [first, mid1, mid2, ..., last]
found = False
valid_moves = []
for mv in legal_moves:
# a valid jump may either have a single jump in it, or
# multiple jumps. In the multiple jump case, start_square is the
# source of the first jump, and dest_square is the endpoint of the
# last jump.
for sq in mv.affected_squares:
if start == mv.affected_squares[FIRST][0] and dest == mv.affected_squares[LAST]:
self._model.make_move(mv, state_copy, False, False)
valid_moves.append(mv)
found = True
break
for move in legal_moves:
if all(sq == move.affected_squares[i][0] for i, sq in enumerate(squares)):
move.annotation = annotation
self._model.make_move(move, state_copy, False, False)
self.moves.append(move)
found = True
break
return found

def _translate_moves_to_board(self, moves: list):
def translate_moves_to_board(self, moves: list):
""" Each move in the file lists the beginning and ending square, along
with an optional annotation string (in Creole fmt) that describes it.
I make sure that each move works on a copy of the model before I commit
Expand All @@ -75,5 +70,5 @@ def _translate_moves_to_board(self, moves: list):
jumped = self._try_jump(squares, annotation, state_copy)
if not jumped:
raise RuntimeError(f"Bad move at index {idx}")
moves.reverse() # TODO: is this still needed?
moves.reverse()
return moves
Loading

0 comments on commit 23de996

Please sign in to comment.