def generate_tree( dot: Digraph, board: Board, node: Union[str, Dict[str, Union[Dict[str, Any], str]]], move_sequence: str = "", ) -> None: board_name = board.get_image_file_name() board_name = board.write_image() dot.node(board_name, label="", shape="plaintext", image=board_name) if isinstance(node, str): if node == "transposition": return child_name = board_name + node dot.node(child_name, node) dot.edge(board_name, child_name) return for move, subtree in node.items(): child = board.do_move(Board.field_to_index(move)) child_name = child.write_image() dot.node(child_name, label="", shape="plaintext", image=child_name) dot.edge(board_name, child_name) move_sequence_prefix = move_sequence + " " + move try: generate_tree(dot, child, subtree, move_sequence + " " + move) except ValueError as e: print(f"at {move_sequence_prefix}: {e}") exit(1)
def from_pgn(cls, filename: str) -> "Game": with open(filename, "r") as file: contents = file.read() game = Game() lines = contents.split("\n") for offset, line in enumerate(lines): if not line.startswith("["): break split_line = line.split(" ") key = split_line[0][1:] value = split_line[1][1:-2] game.metadata[key] = value board = Board() game.boards.append(copy(board)) for line in lines[offset:]: if line == "": continue for word in line.split(" "): if word[0].isdigit(): continue game.moves.append(word) board = board.do_move(board.field_to_index(word)) game.boards.append(copy(board)) return game
def lookup(self, board: Board) -> Optional[Board]: board_id = board.get_normalized_id() openings = self.data["openings"] if board_id not in openings: return None return Board.from_id(openings[board_id]["best_child"])
def test_board_denormalized(bits: int, turn: int) -> None: board = Board.from_discs(bits, 0, turn) normalized, rotation = board.normalized() assert board == normalized.denormalized(rotation) board = Board.from_discs(0, bits, turn) normalized, rotation = board.normalized() assert board == normalized.denormalized(rotation)
def get_openings(self, color: int) -> List[List[Tuple[Board, int]]]: start_positions: Dict[int, List[Board]] = { BLACK: [Board()], WHITE: Board().get_children(), } openings = [] for position in start_positions[color]: openings += self._get_openings(color, position, []) return openings
def test_board_field_index_conversion() -> None: pairs = [(MOVE_PASS, "--")] for x, col in enumerate("abcdefgh"): for y, row in enumerate("12345678"): index = 8 * y + x field = f"{col}{row}" pairs.append((index, field)) for index, field in pairs: assert index == Board.field_to_index(field) assert index == Board.field_to_index(field.upper()) assert field == Board.index_to_field(index)
def test_board_normalized(bits: int, rotation: int, turn: int) -> None: board = Board.from_discs(bits, 0, turn) assert ( Board.from_discs(0x000061928C88FF00, 0, turn), rotation, ) == board.normalized() board = Board.from_discs(0, bits, turn) assert ( Board.from_discs(0, 0x000061928C88FF00, turn), rotation, ) == board.normalized()
def board_dict(board: Board) -> Dict[str, Any]: children = board_details_children(board) return { "id": board.to_id(), "children": children, "stats": { "discs": { "black": board.count(BLACK), "white": board.count(WHITE), }, "moves": len(children), }, }
def board_details(board_id: str) -> Response: try: board = Board.from_id(board_id) except ValueError: return make_response("invalid board id", 400) return jsonify(board_dict(board))
def update_tree_images() -> None: dot = Digraph(format="png") board = Board() with open("white.json", "r") as json_file: tree_root = json.load(json_file) generate_tree(dot, board, tree_root) dot.render("white", cleanup=True) dot = Digraph(format="png") board = Board() with open("black.json", "r") as json_file: tree_root = json.load(json_file) generate_tree(dot, board, tree_root) dot.render("black", cleanup=True)
def board_details_children(board: Board) -> Dict[str, Dict[str, Any]]: children: Dict[str, Dict[str, Any]] = {} for index, field in enumerate(board.get_fields()): if field != VALID_MOVE: continue child = board.do_move(index) # make sure we pass if there are no moves if not child.has_moves(): child = child.do_move(MOVE_PASS) children[str(index)] = { "id": child.to_id(), } return children
def test_board_from_id_ok(id_str: str, expected_board: Board) -> None: board = Board.from_id(id_str) if id_str == "xot": assert BLACK == board.turn assert 12 == board.count(WHITE) + board.count(BLACK) return assert expected_board == board
def validate(self) -> None: for board_id, board_data in self.data["openings"].items(): try: board = Board.from_id(board_id) except ValueError as e: raise OpeningsTreeValidationError( f"board {board_id}: invalid ID") from e child_id = board_data["best_child"] try: Board.from_id(child_id) except ValueError as e: raise OpeningsTreeValidationError( f"board {board_id}: invalid best_child ID") from e if child_id not in board.get_normalized_children_ids(): raise OpeningsTreeValidationError( f"board {board_id}: best_child is not a valid child")
def _get_openings( self, color: int, board: Board, prefix: List[Tuple[Board, int]]) -> List[List[Tuple[Board, int]]]: assert board.turn == color try: best_child_id: str = self.data["openings"][ board.get_normalized_id()]["best_child"] except KeyError: return [prefix] best_child = board.denormalize_child(Board.from_id(best_child_id)) assert best_child in board.get_children() assert best_child.turn == opponent(color) best_move = board.get_move(best_child) openings = [] for grand_child in best_child.get_children(): openings += self._get_openings(color, grand_child, prefix + [(board, best_move)]) return openings
def add_board_interactive(self, board: Board, game: Game, move_offset: int) -> Board: board.show() print() move_sequence = " ".join(game.moves[:move_offset]) print(f"Replay: {move_sequence}") move_fields = board.get_move_fields() print("Enter correct move:") while True: field = input("> ") if field in move_fields: break best_move = Board.field_to_index(field) best_child = board.do_move(best_move).normalized()[0] board_normalized = board.normalized()[0] self.upsert(board_normalized, best_child) return best_child
def show(board_id: str) -> None: board = Board.from_id(board_id) board.show()
def test_board_to_id() -> None: assert "B00000008100000000000001008000000" == Board().to_id()
def test_get_fields() -> None: expected = [EMPTY] * 64 expected[28] = expected[35] = BLACK expected[27] = expected[36] = WHITE expected[19] = expected[26] = expected[37] = expected[44] = VALID_MOVE assert expected == Board().get_fields()
def test_board_image_file_name() -> None: assert "jpg/00000008100000000000001008000000.jpg" == Board().get_image_file_name()
def test_board_black(board: Board, expected_black: int, expected_white: int) -> None: assert expected_black == board.black() assert expected_white == board.white()
def test_board_from_id_fail(id_str: str, expected_error_message: str) -> None: with pytest.raises(ValueError) as exc_info: Board.from_id(id_str) assert expected_error_message == exc_info.value.args[0]
def test_board_init() -> None: board = Board() assert BLACK == board.turn assert 1 << 28 | 1 << 35 == board.me assert 1 << 27 | 1 << 36 == board.opp
def test_board_from_xot() -> None: board = Board.from_xot() assert BLACK == board.turn assert 12 == board.count(WHITE) + board.count(BLACK)
def test_board_from_discs(me: int, opp: int, turn: bool) -> None: board = Board.from_discs(me, opp, turn) assert me == board.me assert opp == board.opp assert turn == board.turn
def upsert(self, board: Board, best_child: Board) -> None: board_id = board.get_normalized_id() best_child_id = best_child.get_normalized_id() self.data["openings"][board_id] = {"best_child": best_child_id}
board = Board.from_discs(me, opp, turn) assert me == board.me assert opp == board.opp assert turn == board.turn def test_board_from_xot() -> None: board = Board.from_xot() assert BLACK == board.turn assert 12 == board.count(WHITE) + board.count(BLACK) @pytest.mark.parametrize( ["id_str", "expected_board"], ( ["initial", Board()], ["xot", None], ["B00000000000000010000000000000002", Board.from_discs(1, 2, BLACK)], ["W00000000000000010000000000000002", Board.from_discs(2, 1, WHITE)], [ "Bffffffffffffffffffffffffffffffff", Board.from_discs(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, BLACK), ], ), ) def test_board_from_id_ok(id_str: str, expected_board: Board) -> None: board = Board.from_id(id_str) if id_str == "xot": assert BLACK == board.turn assert 12 == board.count(WHITE) + board.count(BLACK)