From abe0b1ec1d8aeb9e7f75f208fa013856c74a5bc2 Mon Sep 17 00:00:00 2001 From: Brandon Corfman Date: Sun, 6 Nov 2022 21:26:09 -0500 Subject: [PATCH] Draft of completed load/save --- base/move.py | 3 + game/checkers.py | 2 +- game/gamemanager.py | 56 +++++++++++------ parsing/PDN.py | 100 ++++++++++++++++++++---------- parsing/gamepersist.py | 22 ------- parsing/migrate.py | 22 ++++--- tests/test_PDN.py | 137 +++++++++++++++++++++++++++++++++-------- util/globalconst.py | 10 --- 8 files changed, 233 insertions(+), 119 deletions(-) delete mode 100644 parsing/gamepersist.py diff --git a/base/move.py b/base/move.py index 354e9e1..91f74f9 100644 --- a/base/move.py +++ b/base/move.py @@ -3,5 +3,8 @@ def __init__(self, squares, annotation=""): self.affected_squares = squares self.annotation = annotation + def __eq__(self, other): + return self.affected_squares == other.affected_squares + def __repr__(self): return str(self.affected_squares) diff --git a/game/checkers.py b/game/checkers.py index 851fdc6..f62718a 100644 --- a/game/checkers.py +++ b/game/checkers.py @@ -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: diff --git a/game/gamemanager.py b/game/gamemanager.py index 0fdc353..841fffa 100644 --- a/game/gamemanager.py +++ b/game/gamemanager.py @@ -2,14 +2,15 @@ from tkinter import Label, SUNKEN, NW from tkinter.filedialog import askopenfilename, asksaveasfilename from tkinter.messagebox import askyesnocancel, showerror -from util.globalconst import BLACK, WHITE, TITLE, VERSION, KING, MAN, PROGRAM_TITLE, TRAINING_DIR -from util.globalconst import square_map, keymap +from datetime import datetime from game.checkers import Checkers from gui.boardview import BoardView from gui.filelist import FileList from gui.playercontroller import PlayerController from gui.alphabetacontroller import AlphaBetaController -from parsing.PDN import PDNReader +from parsing.PDN import PDNReader, PDNWriter +from util.globalconst import BLACK, WHITE, TITLE, VERSION, KING, MAN, PROGRAM_TITLE, TRAINING_DIR +from util.globalconst import square_map, keymap class GameManager(object): @@ -174,32 +175,51 @@ def save_game(self): def _write_file(self, filename): try: - saved_game = SavedGame() + # redo moves up to the end of play, if possible + to_move = self.model.curr_state.to_move + scoring = self.model.utility(to_move, self.model.curr_state) + if scoring == -4000: + if to_move == WHITE: + result = "1-0" + else: + result = "0-1" + elif scoring == 4000: + if to_move == WHITE: + result = "0-1" + else: + result = "1-0" + else: + result = "*" # undo moves back to the beginning of play undo_steps = 0 while self.model.curr_state.undo_list: undo_steps += 1 self.model.curr_state.undo_move(None, True, True, self.view.get_annotation()) # save the state of the board - saved_game.to_move = self.model.curr_state.to_move - saved_game.num_players = self.num_players - saved_game.black_men = [] - saved_game.black_kings = [] - saved_game.white_men = [] - saved_game.white_kings = [] + to_move = self.model.curr_state.to_move + black_men = [] + black_kings = [] + white_men = [] + white_kings = [] for i, sq in enumerate(self.model.curr_state.squares): if sq == BLACK | MAN: - saved_game.black_men.append(keymap[i]) + black_men.append(keymap[i]) elif sq == BLACK | KING: - saved_game.black_kings.append(keymap[i]) + black_kings.append(keymap[i]) elif sq == WHITE | MAN: - saved_game.white_men.append(keymap[i]) + white_men.append(keymap[i]) elif sq == WHITE | KING: - saved_game.white_kings.append(keymap[i]) - # saved_game.description = self.view.serializer.dump() - saved_game.moves = self.model.curr_state.redo_list - saved_game.flip_board = self.view.flip_view - saved_game.write(filename) + white_kings.append(keymap[i]) + description = self.view.serializer.dump() + moves = self.model.curr_state.redo_list + annotations = [] + board_orientation = "white_on_top" if self.view.flip_view is False else "black_on_top" + black_player = "Player1" + white_player = "Player2" + PDNWriter.to_file(filename, '*', '*', datetime.now().strftime("%d/%m/%Y"), '*', black_player, white_player, + to_move, black_men, white_men, black_kings, white_kings, result, board_orientation, + description, moves, annotations) + # redo moves forward to the previous state for i in range(undo_steps): annotation = self.view.get_annotation() diff --git a/parsing/PDN.py b/parsing/PDN.py index 8e3416f..53a5290 100644 --- a/parsing/PDN.py +++ b/parsing/PDN.py @@ -6,8 +6,9 @@ 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 base.move import Move from game.checkers import Checkers -from util.globalconst import BLACK_IDX, WHITE_IDX, square_map +from util.globalconst import keymap, square_map, WHITE, BLACK def _is_move(delta): @@ -20,7 +21,7 @@ def _removeLineFeed(s): Game = NamedTuple("Game", [("event", str), ("site", str), ("date", str), ("round", str), ("black_player", str), ("white_player", str), - ("next_to_move", str), ("black_men", list), ("white_men", list), + ("next_to_move", int), ("black_men", list), ("white_men", list), ("black_kings", list), ("white_kings", list), ("result", str), ("board_orientation", str), ("description", str), ("moves", list)]) @@ -188,7 +189,7 @@ def _set_board_defaults_if_needed(self): if self._flip_board is None: self._flip_board = "white_on_top" if self._next_to_move is None: - self._next_to_move = "black" + self._next_to_move = BLACK if self._black_men is None: self._black_men = list(range(1, 13)) self._black_kings = [] @@ -235,6 +236,7 @@ def read_game(self, idx): "FEN": self._read_fen, "BoardOrientation": self._read_board_orientation} + self._reset_pdn_vars() if not self._game_indexes: self.get_game_list() self._stream.seek(self._game_indexes[idx]) @@ -272,14 +274,14 @@ def read_game(self, idx): 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._translate_moves_to_board(self._moves)) + self._description, self._PDN_to_board(self._moves)) def _get_player_to_move(self, turn): turn = turn.upper() if turn == "B": - result = "black" + result = BLACK elif turn == "W": - result = "white" + result = WHITE else: raise SyntaxError("Unknown turn type {turn} in FEN") return result @@ -312,26 +314,29 @@ 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): + def _PDN_to_board(self, pdn_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) - + # remove last item in list before processing if it's a single element + # (i.e., game separator) + last_move, last_annotation = pdn_moves[-1] + if len(last_move) == 1: + pdn_moves.pop() # analyze squares to perform a move or jump. idx = 0 - moves_len = len(moves) + moves_len = len(pdn_moves) translated_moves = [] while idx < moves_len: - squares, annotation = moves[idx] + squares, annotation = pdn_moves[idx] delta = squares[0] - squares[1] if _is_move(delta): move = self._try_move(squares, annotation, state_copy) @@ -350,10 +355,10 @@ def _translate_moves_to_board(self, moves: list): return translated_moves -def _translate_to_fen(next_to_move, black_men, white_men, black_kings, white_kings): - if next_to_move.lower() == "black": +def translate_to_fen(next_to_move, black_men, white_men, black_kings, white_kings): + if next_to_move == 'black': fen = "B:" - elif next_to_move.lower() == "white": + elif next_to_move == 'white': fen = "W:" else: raise RuntimeError(f"Unknown player {next_to_move} in next_to_move variable") @@ -384,18 +389,14 @@ def _translate_to_text(move): return sep.join([str(n) for n in move]) moves.reverse() - if annotations: - annotations.reverse() + annotations.reverse() movetext = "" movenum = 0 while True: if len(moves) == 0: break item = moves.pop() - if annotations: - anno = annotations.pop() - else: - anno = ["", ""] + anno = annotations.pop() if len(item) == 1: movetext += item.pop() @@ -408,30 +409,30 @@ def _translate_to_text(move): # will be replaced with spaces. movetext += f"{movenum}.`" black_move = item.pop() - comment = anno.pop() - if comment and comment[0] in ['!', '?']: - tokens = comment.split(maxsplit=1) + black_comment = anno.pop() + if black_comment and black_comment[0] in ['!', '?']: + tokens = black_comment.split(maxsplit=1) black_strength = tokens[0] - comment = tokens[1] if len(tokens) > 1 else "" + black_comment = tokens[1] if len(tokens) > 1 else "" else: black_strength = "" movetext += f"{_translate_to_text(black_move)}{black_strength}" - if comment: - movetext += " {" + f"{comment}" + "} " + if black_comment: + movetext += " {" + f"{black_comment}" + "} " else: movetext += "`" if item: white_move = item.pop() - comment = anno.pop() - if comment and comment[0] in ['!', '?']: - tokens = comment.split(maxsplit=1) + white_comment = anno.pop() + if white_comment and white_comment[0] in ['!', '?']: + tokens = white_comment.split(maxsplit=1) white_strength = tokens[0] - comment = tokens[1] if len(tokens) > 1 else "" + white_comment = tokens[1] if len(tokens) > 1 else "" else: white_strength = "" movetext += f"{_translate_to_text(white_move)}{white_strength}" - if comment: - movetext += " {" + f"{comment}" + "}" + if white_comment: + movetext += " {" + f"{white_comment}" + "}" movetext += " " return movetext @@ -459,13 +460,15 @@ def _write(self, event: str, site: str, date: str, rnd: str, black_player: str, frozenset(white_men) != frozenset(range(21, 33))): self.stream.write('[SetUp "1"]') self.stream.write(f'[FEN "{result}"]\n') - for line in self._wrapper.wrap(_translate_to_fen(next_to_move, black_men, white_men, black_kings, - white_kings)): + for line in self._wrapper.wrap(translate_to_fen(next_to_move, black_men, white_men, black_kings, + white_kings)): self.stream.write(line + '\n') self.stream.write(f'[BoardOrientation "{board_orientation}"]\n') if description: for line in description: self.stream.write(line) + if annotations is None: + annotations = [["", ""] for _ in moves] for line in self._wrapper.wrap(_translate_to_movetext(moves, annotations)): line = line.replace("`", " ") # NOTE: see _translate_to_movetext function self.stream.write(line + '\n') @@ -490,3 +493,32 @@ def to_stream(cls, stream, event, site, date, rnd, black_player, white_player, n black_kings, white_kings, result, board_orientation, moves, annotations=None, description=""): cls(stream, event, site, date, rnd, black_player, white_player, next_to_move, black_men, white_men, black_kings, white_kings, result, board_orientation, description, moves, annotations) + + +def board_to_PDN(board_moves: list[Move]): + pdn_moves = "" + move_count = 1 + for idx, move in enumerate(board_moves): + num_squares = len(move.affected_squares) + if idx % 2 == 0: + pdn_moves += f"{move_count}. " + move_count += 1 + if num_squares == 2: # move + sq1 = keymap[move.affected_squares[0][0]] + sq2 = keymap[move.affected_squares[1][0]] + pdn_moves += f"{sq1}-{sq2}" + elif num_squares >= 3: # jump + for i in range(0, num_squares-2, 2): + sq = keymap[move.affected_squares[i][0]] + pdn_moves += f"{sq}x" + sq = keymap[move.affected_squares[-1][0]] + pdn_moves += f"{sq}" + else: + raise RuntimeError("unknown number of affected_squares") + if move.annotation: + pdn_moves += " {" + f"{move.annotation}" + "}" + if idx % 2 == 1: + pdn_moves += " " + else: + pdn_moves += " " + return pdn_moves.rstrip() diff --git a/parsing/gamepersist.py b/parsing/gamepersist.py deleted file mode 100644 index 280a61c..0000000 --- a/parsing/gamepersist.py +++ /dev/null @@ -1,22 +0,0 @@ -import copy -from util.globalconst import square_map -from game.checkers import Checkers, Checkerboard - - -class SavedGame(object): - def __init__(self): - self._model = Checkers() - self.to_move = None - self.moves = [] - self.description = '' - self.black_men = [] - self.white_men = [] - self.black_kings = [] - self.white_kings = [] - self.flip_board = False - self.num_players = 1 - self._bm_check = False - self._bk_check = False - self._wm_check = False - self._wk_check = False - diff --git a/parsing/migrate.py b/parsing/migrate.py index 408bfbb..fefe814 100644 --- a/parsing/migrate.py +++ b/parsing/migrate.py @@ -1,8 +1,9 @@ import os.path -from pathlib import Path from datetime import datetime, timezone +from util.globalconst import BLACK, WHITE from io import StringIO from parsing.PDN import Game, PDNWriter +from pathlib import Path class RCF2PDN: @@ -44,6 +45,8 @@ def translate(self, rcf_stream, pdn_stream, event_name=None, file_mod_time=None) if self._validate_input(): self._transform_input() self._write_output(pdn_stream) + else: + raise RuntimeError("RCF input not valid") def _read_input(self, rcf_stream): rcf_tags = {'': self._read_description, @@ -66,6 +69,7 @@ def _transform_input(self): site = "*" date = self.file_mod_time or datetime.now().strftime("%d/%m/%Y") rnd = "*" + next_to_move = BLACK if self.next_to_move == 'black' else WHITE black_player = "Player1" white_player = "Player2" result = self._get_game_result() @@ -76,15 +80,15 @@ def _transform_input(self): description = "" for line in self.description: description += f"; {line}" - self._game = Game(event, site, date, rnd, black_player, white_player, self.next_to_move, list(self.black_men), - list(self.white_men), list(self.black_kings), list(self.white_kings), result, orientation, - description, self.moves, self.annotations) + self._game = Game(event, site, date, rnd, black_player, white_player, next_to_move, list(self.black_men), + list(self.white_men), list(self.black_kings), list(self.white_kings), result, + orientation, description, self.moves) def _write_output(self, pdn_stream): game = self._game PDNWriter.to_stream(pdn_stream, game.event, game.site, game.date, game.round, game.black_player, game.white_player, game.next_to_move, game.black_men, game.white_men, game.black_kings, - game.white_kings, game.result, game.board_orientation, game.moves, game.annotations, + game.white_kings, game.result, game.board_orientation, game.moves, self.annotations, game.description) def _get_game_result(self): @@ -106,10 +110,10 @@ def _get_game_moves(self): start, dest = move.split("-") strength = self._get_move_strength(i) if i == 0: - movetext += f"{i+1}." + movetext += f"{i + 1}." elif i % 2 == 0: - movetext += f" {i+1}." - if abs(int(start)-int(dest)) <= 5: + movetext += f" {i + 1}." + if abs(int(start) - int(dest)) <= 5: movetext += f" {start}-{dest}{strength}" # regular move else: movetext += f" {start}x{dest}{strength}" # jump @@ -234,6 +238,6 @@ def _read_white_kings(self, line): self.white_kings = [int(i) for i in line.split()[1:]] def _validate_input(self): - return (self.num_players in range(3) and self.next_to_move in ['black', 'white'] and + return (self.num_players in range(3) and self.next_to_move in ["black", "white"] and self.flip_board in range(2) and self.moves and (self.black_men or self.black_kings or self.white_men or self.white_kings)) diff --git a/tests/test_PDN.py b/tests/test_PDN.py index f97723c..aa1b3be 100644 --- a/tests/test_PDN.py +++ b/tests/test_PDN.py @@ -1,5 +1,51 @@ import os -from parsing.PDN import PDNReader, PDNWriter, _translate_to_fen +from base.move import Move +from parsing.PDN import PDNReader, PDNWriter, translate_to_fen, board_to_PDN +from util.globalconst import BLACK, WHITE, MAN, KING, FREE, square_map + + +def test_board_to_PDN(): + board_moves = [Move([[23, BLACK | MAN, FREE], [29, WHITE | MAN, FREE], [35, FREE, FREE], [40, WHITE | MAN, FREE], + [45, FREE, BLACK | KING]], + 'Black double jump taking out two white men and landing in the king row')] + pdn_moves = '1. 16x23x32 {Black double jump taking out two white men and landing in the king row}' + assert board_to_PDN(board_moves) == pdn_moves + # First moves from Ballot 1, trunk line in Complete Checkers by Richard Pask + board_moves = [Move([[square_map[9], BLACK | MAN, FREE], [square_map[13], FREE, BLACK | MAN]], ""), + Move([[square_map[21], WHITE | MAN, FREE], [square_map[17], FREE, WHITE | MAN]], ""), + Move([[square_map[5], BLACK | MAN, FREE], [square_map[9], FREE, BLACK | MAN]], ""), + Move([[square_map[25], WHITE | MAN, FREE], [square_map[21], FREE, WHITE | MAN]], ""), + Move([[square_map[11], BLACK | MAN, FREE], [square_map[15], FREE, BLACK | MAN]], ""), + Move([[square_map[29], WHITE | MAN, FREE], [square_map[25], FREE, WHITE | MAN]], ""), + Move([[square_map[9], BLACK | MAN, FREE], [square_map[14], FREE, BLACK | MAN]], ""), + Move([[square_map[23], WHITE | MAN, FREE], [square_map[18], FREE, WHITE | MAN]], ""), + Move([[square_map[14], BLACK | MAN, FREE], [square_map[18], WHITE | MAN, FREE], + [square_map[23], FREE, BLACK | MAN]], ""), + Move([[square_map[27], WHITE | MAN, FREE], [square_map[23], BLACK | MAN, FREE], + [square_map[18], FREE, FREE], [square_map[15], BLACK | MAN, FREE], + [square_map[11], FREE, WHITE | MAN]], ""), + Move([[square_map[8], BLACK | MAN, FREE], [square_map[11], WHITE | MAN, FREE], + [square_map[15], FREE, BLACK | MAN]], ""), + Move([[square_map[17], WHITE | MAN, FREE], [square_map[14], FREE, WHITE | MAN]], ""), + Move([[square_map[10], BLACK | MAN, FREE], [square_map[14], WHITE | MAN, FREE], + [square_map[17], FREE, BLACK | MAN]], ""), + Move([[square_map[21], WHITE | MAN, FREE], [square_map[17], BLACK | MAN, FREE], + [square_map[14], FREE, WHITE | MAN]], ""), + Move([[square_map[12], BLACK | MAN, FREE], [square_map[16], FREE, BLACK | MAN]], ""), + Move([[square_map[24], WHITE | MAN, FREE], [square_map[20], FREE, WHITE | MAN]], + "26-23; 16-19 23-16; 7-11 16-7 3-26 30-23; 4-8 25-22; 8-11 24-19; 15-24 28-19; 6-10 to a draw"), + Move([[square_map[16], BLACK | MAN, FREE], [square_map[19], FREE, BLACK | MAN]], ""), + Move([[square_map[25], WHITE | MAN, FREE], [square_map[21], FREE, WHITE | MAN]], + "32-27; 4-8 25-21 same"), + Move([[square_map[4], BLACK | MAN, FREE], [square_map[8], FREE, BLACK | MAN]], ""), + Move([[square_map[32], WHITE | MAN, FREE], [square_map[27], FREE, WHITE | MAN]], ""), + Move([[square_map[8], BLACK | MAN, FREE], [square_map[12], FREE, BLACK | MAN]], ""), + Move([[square_map[27], WHITE | MAN, FREE], [square_map[24], FREE, WHITE | MAN]], "")] + pdn_moves = '1. 9-13 21-17 2. 5-9 25-21 3. 11-15 29-25 4. 9-14 23-18 5. 14x23 27x18x11 '\ + '6. 8x15 17-14 7. 10x17 21x14 8. 12-16 24-20 {26-23; 16-19 23-16; 7-11 16-7 '\ + '3-26 30-23; 4-8 25-22; 8-11 24-19; 15-24 28-19; 6-10 to a draw} 9. 16-19 25-21 '\ + '{32-27; 4-8 25-21 same} 10. 4-8 32-27 11. 8-12 27-24' + assert board_to_PDN(board_moves) == pdn_moves def test_parse_PDN_string_success(): @@ -36,26 +82,67 @@ def test_parse_PDN_string_success(): assert game.white_player == "Jubai" assert game.black_player == "windycity" assert game.result == "1-0" - assert game.moves == [[[10, 15], [23, 18]], [[7, 10], [22, 17]], - [[15, 22], [25, 18]], [[11, 15], [18, 11]], [[8, 15], [24, 20]], - [[4, 8], [27, 23]], [[9, 13], [20, 16]], [[13, 22], [26, 17]], - [[12, 19, 26], [30, 23]], [[3, 7], [23, 19]], - [[15, 24], [28, 19]], [[7, 11], [17, 14]], [[10, 17], [21, 14]], - [[2, 7], [32, 27]], [[8, 12], [29, 25]], [[6, 9], [31, 26]], - [[9, 18], [19, 15]], [[7, 10], [15, 8]], [[5, 9], [26, 22]], - [[10, 15], [22, 17]], [[1, 6], [17, 14]], [[9, 13], [14, 10]], - [[6, 9], [8, 3]], [[15, 19], [3, 7]], [[19, 23], [27, 24]], - [[23, 27], [24, 20]], [[13, 17], [25, 21]], [[17, 22], [21, 17]], - [[18, 23], [17, 13]], [[9, 14], [10, 6]], [[14, 17], [6, 2]], - [[23, 26], [7, 10]], [[22, 25], [10, 14]], [[17, 21], [14, 18]], - [[26, 31], [13, 9]], [[25, 30], [18, 22]], [[27, 32], [9, 5]], - [[30, 26], [22, 18]], [[26, 23], [18, 27]], [[32, 23], [5, 1]], - [[21, 25], [2, 7]], [[25, 30], [7, 10]], [[31, 26], [10, 15]], - [[26, 31], [1, 6]], [[30, 25], [6, 9]], [[23, 26], [9, 14]], - [[26, 22], [15, 19]], [[25, 21], [20, 16]], [[22, 17], [14, 18]], - [[17, 22], [18, 25]], [[21, 30], [16, 11]], [[31, 26], [11, 7]], - [[26, 23], [19, 26]], [[30, 23], [7, 3]], [[23, 18], [3, 8]], - [[18, 15], ["1-0"]]] + board_moves = [Move(m) for m in [[[30, 9, 16], [24, 16, 9]], [[7, 10, 16], [12, 16, 10]], + [[35, 9, 16], [30, 16, 9]], + [[13, 6, 16], [7, 16, 10]], [[47, 9, 16], [41, 10, 16], [35, 16, 9]], + [[29, 10, 16], [35, 9, 16], [41, 16, 10]], [[41, 9, 16], [35, 16, 9]], + [[18, 6, 16], [13, 16, 6]], [[46, 9, 16], [41, 16, 9]], [[23, 6, 16], [18, 16, 6]], + [[37, 9, 16], [42, 10, 16], [47, 16, 9]], + [[30, 10, 16], [36, 9, 16], [42, 16, 10]], + [[31, 9, 16], [36, 16, 9]], [[25, 10, 16], [30, 16, 10]], + [[36, 9, 16], [31, 16, 9]], + [[28, 6, 16], [23, 16, 6]], [[42, 9, 16], [37, 16, 9]], + [[24, 10, 16], [29, 16, 10]], + [[41, 9, 16], [36, 16, 9]], [[20, 10, 16], [25, 16, 10]], + [[35, 9, 16], [41, 16, 9]], + [[14, 10, 16], [20, 16, 10]], [[47, 9, 16], [42, 16, 9]], + [[9, 10, 16], [14, 16, 10]], + [[41, 9, 16], [46, 16, 9]], [[19, 10, 16], [24, 16, 10]], + [[46, 9, 16], [41, 16, 9]], + [[13, 10, 16], [19, 16, 10]], [[42, 5, 16], [47, 16, 9]], + [[8, 10, 16], [13, 16, 10]], + [[37, 5, 16], [42, 16, 5]], [[15, 6, 16], [9, 16, 10]], + [[45, 9, 16], [40, 10, 16], [35, 16, 9]], + [[30, 10, 16], [35, 9, 16], [40, 16, 10]], + [[41, 9, 16], [35, 16, 9]], [[36, 10, 16], [30, 16, 10]], + [[47, 9, 16], [41, 16, 9]], + [[20, 6, 16], [15, 16, 6]], [[40, 5, 16], [45, 16, 9]], + [[30, 10, 16], [36, 16, 10]], + [[42, 5, 16], [47, 16, 9]], [[26, 6, 16], [20, 16, 6]], [[41, 5, 16], [46, 16, 9]], + [[25, 10, 16], [30, 16, 10]], [[31, 5, 16], [37, 16, 5]], + [[19, 10, 16], [25, 16, 10]], + [[36, 5, 16], [42, 16, 5]], [[13, 10, 16], [19, 16, 10]], + [[35, 5, 16], [41, 16, 5]], + [[14, 6, 16], [8, 16, 10]], [[25, 5, 16], [31, 16, 5]], [[19, 6, 16], [14, 16, 6]], + [[20, 5, 16], [25, 16, 5]], [[31, 6, 16], [26, 16, 6]], [[30, 5, 16], [35, 16, 5]], + [[37, 6, 16], [31, 16, 6]], [[31, 5, 16], [36, 16, 5]], [[42, 6, 16], [37, 16, 6]], + [[26, 5, 16], [31, 16, 5]], [[34, 6, 16], [28, 16, 6]], [[35, 5, 16], [40, 16, 5]], + [[40, 6, 16], [34, 16, 6]], [[29, 5, 16], [35, 16, 5]], + [[7, 10, 16], [13, 16, 10]], + [[24, 5, 16], [29, 16, 5]], [[12, 6, 16], [7, 16, 10]], [[14, 5, 16], [20, 16, 5]], + [[25, 6, 16], [19, 16, 6]], [[20, 5, 16], [26, 16, 5]], [[31, 6, 16], [25, 16, 6]], + [[9, 5, 16], [14, 16, 5]], [[36, 6, 16], [31, 16, 6]], [[19, 5, 16], [24, 16, 5]], + [[41, 6, 16], [36, 16, 6]], [[15, 5, 16], [20, 16, 5]], + [[24, 6, 16], [18, 5, 16], [12, 16, 6]], [[13, 5, 16], [19, 16, 5]], + [[29, 6, 16], [24, 16, 6]], [[20, 5, 16], [25, 6, 16], [30, 16, 5]], + [[46, 6, 16], [41, 16, 6]], [[14, 5, 16], [20, 16, 5]], [[48, 6, 16], [42, 16, 6]], + [[12, 5, 16], [17, 16, 5]], [[45, 6, 16], [40, 16, 6]], [[8, 5, 16], [13, 16, 5]], + [[37, 6, 16], [31, 5, 16], [25, 16, 6]], [[19, 5, 16], [25, 6, 16], [31, 16, 5]], + [[31, 6, 16], [25, 16, 6]], [[13, 5, 16], [18, 16, 5]], + [[39, 6, 16], [34, 5, 16], [29, 16, 6]], + [[24, 5, 16], [29, 6, 16], [34, 16, 5]], + [[35, 6, 16], [29, 16, 6]], [[7, 5, 16], [13, 16, 5]], + [[47, 6, 16], [41, 5, 16], [35, 16, 6]], + [[17, 5, 16], [23, 6, 16], [29, 16, 16], [35, 6, 16], [41, 16, 5]], + [[41, 6, 16], [36, 5, 16], [31, 16, 6]], [[26, 5, 16], [31, 6, 16], [36, 16, 5]], + [[28, 6, 16], [23, 16, 6]], [[20, 5, 16], [26, 16, 5]], [[40, 6, 16], [35, 16, 6]], + [[6, 5, 16], [12, 16, 5]], [[34, 6, 16], [28, 16, 6]], + [[12, 5, 16], [18, 6, 16], [24, 16, 5]], [[30, 6, 16], [24, 5, 16], [18, 16, 6]], + [[18, 5, 16], [24, 16, 5]], [[42, 6, 16], [36, 5, 16], [30, 16, 6]], + [[24, 5, 16], [30, 6, 16], [36, 16, 5]], [[36, 6, 16], [31, 16, 6]], + [[13, 5, 16], [19, 16, 5]], [[35, 6, 16], [30, 16, 6]], + [[19, 5, 16], [24, 16, 5]]]] + assert game.moves == board_moves def test_parse_PDN_file_success(): @@ -129,9 +216,9 @@ def test_write_PDN_string_success(): '[BoardOrientation "white_on_top"]\n' + \ '1. 11-15 22-17 2. 15-19 24x15 3. 10x19 23x16 4. 12x19 25-22 5. 7-10 27-24 6. 10-15 17-13 7. 9-14 29-25 ' \ '8. 6-10 22-17\n' \ - '9. 1-6 26-23 10. 19x26 30x23 11. 8-11 24-19 12. 15x24 28x19 13. 3-7 25-22 14. 11-15 32-28 15. 15x24 '\ + '9. 1-6 26-23 10. 19x26 30x23 11. 8-11 24-19 12. 15x24 28x19 13. 3-7 25-22 14. 11-15 32-28 15. 15x24 ' \ '28x19\n' + \ - '16. 7-11 19-16 17. 11x20 23-19 18. 14-18 22x15 19. 4-8 31-27 20. 5-9 27-23 21. 9-14 19-16 22. 10x19x26 '\ + '16. 7-11 19-16 17. 11x20 23-19 18. 14-18 22x15 19. 4-8 31-27 20. 5-9 27-23 21. 9-14 19-16 22. 10x19x26 ' \ '17x10x1 1/2-1/2\n' @@ -142,5 +229,5 @@ def test_translate_to_fen(): white_men = [26] black_kings = [30] white_kings = [27] - assert _translate_to_fen(next_to_move, black_men, white_men, black_kings, white_kings) == \ - "W:W26,K27:B17,K30" + assert translate_to_fen(next_to_move, black_men, white_men, black_kings, white_kings) == \ + "W:W26,K27:B17,K30" diff --git a/util/globalconst.py b/util/globalconst.py index 633272c..da3eb01 100644 --- a/util/globalconst.py +++ b/util/globalconst.py @@ -131,16 +131,6 @@ 8: set(), 9: set()} -# (white) -# 45 46 47 48 -# 39 40 41 42 -# 34 35 36 37 -# 28 29 30 31 -# 23 24 25 26 -# 17 18 19 20 -# 12 13 14 15 -# 6 7 8 9 -# (black) # other squares reachable from a particular square with a black man BLACK_MAP = {6: {12, 17, 18, 23, 24, 28, 29, 30, 34, 35, 36, 39, 40, 41, 42, 45, 46, 47, 48}, 7: {12, 13, 17, 18, 19, 23, 24, 25, 28, 29, 30, 31, 34, 35, 36, 37, 39, 40, 41, 42, 45, 46, 47, 48},