Skip to content

Commit

Permalink
incomplete draft of load_game.
Browse files Browse the repository at this point in the history
  • Loading branch information
bcorfman committed Nov 3, 2022
1 parent d8162d1 commit 49034fa
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 60 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ mainframe.dist/
raven.ini
*.pyc
pdm.lock
.python-version
2 changes: 1 addition & 1 deletion game/checkers.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def _get_captures(self):
player = self.to_move
enemy = self.enemy
squares = self.squares
valid_indices = WHITE_IDX if player == WHITE else BLACK_IDX
valid_indices = WHITE_IDX if player == 'white' else BLACK_IDX
all_captures = []
for i in self.valid_squares:
if squares[i] & player and squares[i] & MAN:
Expand Down
7 changes: 3 additions & 4 deletions game/gamemanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from gui.filelist import FileList
from gui.playercontroller import PlayerController
from gui.alphabetacontroller import AlphaBetaController
from parsing.gamepersist import SavedGame
from parsing.PDN import PDNReader


Expand All @@ -29,6 +28,7 @@ def __init__(self, **props):
self.controller1 = None
self.controller2 = None
self.set_controllers()
# noinspection PyUnresolvedReferences
self.controller1.start_turn()
self.filepath = None

Expand Down Expand Up @@ -112,7 +112,6 @@ 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,12 +129,12 @@ 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 = sg.moves
self.model.curr_state.redo_list = game.moves
self.model.curr_state.update_piece_count()
self.view.reset_view(self.model)
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.flip_board(game.board_orientation == "white_on_top")
self.view.update_statusbar()
self.parent.set_title_bar_filename(os.path.basename(filepath))
self.filepath = filepath
Expand Down
65 changes: 62 additions & 3 deletions parsing/PDN.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import charset_normalizer
import copy
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
from typing import NamedTuple
from game.checkers import Checkers
from util.globalconst import BLACK_IDX, WHITE_IDX, square_map


def _is_move(delta):
return delta in [3, 4, 5, -3, -4, -5]


def _removeLineFeed(s):
Expand Down Expand Up @@ -57,6 +63,7 @@ def _removeLineFeed(s):
class PDNReader:
def __init__(self, stream, source=""):
self._stream = stream
self._model = Checkers()
self._source = f"in {source}" if source else ""
self._stream_pos = 0
self._lineno = 0
Expand Down Expand Up @@ -262,11 +269,10 @@ 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, sg.translate_moves_to_board(self._moves))
self._description, self._translate_moves_to_board(self._moves))

def _get_player_to_move(self, turn):
turn = turn.upper()
Expand All @@ -290,6 +296,59 @@ def _get_player_pieces(self, fen_line: str):
men.append(int(sq))
return men, kings

def _try_move(self, squares: list, annotation: str, state_copy: Checkers):
board_squares = [square_map[sq] for sq in squares]
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 sq_len == len(move.affected_squares) and \
all(sq == move.affected_squares[i][0] for i, sq in enumerate(board_squares)):
move.annotation = annotation
self._model.make_move(move, state_copy, False, False)
return move

def _try_jump(self, squares: list, annotation: str, state_copy: Checkers):
board_squares = [square_map[sq] for sq in squares]
if self._model.captures_available(state_copy):
legal_moves = self._model.legal_moves(state_copy)
sq_len = len(squares)
for move in legal_moves:
if all(sq == move.affected_squares[i*2][0] for i, sq in enumerate(board_squares)):
move.annotation = annotation
self._model.make_move(move, state_copy, False, False)
return move

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
to using it inside the code. """
state_copy = copy.deepcopy(self._model.curr_state)

# analyze squares to perform a move or jump.
idx = 0
moves_len = len(moves)
translated_moves = []
while idx < moves_len:
squares, annotation = moves[idx]
delta = squares[0] - squares[1]
if _is_move(delta):
move = self._try_move(squares, annotation, state_copy)
if move:
translated_moves.append(move)
else:
raise RuntimeError(f"Illegal move {squares} found")
else:
jump = self._try_jump(squares, annotation, state_copy)
if jump:
translated_moves.append(jump)
else:
raise RuntimeError(f"Illegal jump {squares} found")
idx += 1
translated_moves.reverse()
return translated_moves


def _translate_to_fen(next_to_move, black_men, white_men, black_kings, white_kings):
if next_to_move.lower() == "black":
Expand Down
52 changes: 0 additions & 52 deletions parsing/gamepersist.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,55 +20,3 @@ def __init__(self):
self._wm_check = False
self._wk_check = False

def _is_move(self, delta):
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 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)
return True
else:
raise RuntimeError(f"Illegal move {squares} found")

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)
found = False
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):
""" 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
to using it inside the code. """
state_copy = copy.deepcopy(self._model.curr_state)

# analyze squares to perform a move or jump.
idx = 0
moves_len = len(moves)
while idx < moves_len:
squares, annotation = moves[idx]
delta = abs(squares[0] - squares[1])
if self._is_move(delta):
self._try_move(squares, annotation, state_copy)
else:
jumped = self._try_jump(squares, annotation, state_copy)
if not jumped:
raise RuntimeError(f"Bad move at index {idx}")
moves.reverse()
return moves

0 comments on commit 49034fa

Please sign in to comment.