From 65f6d7d7a2229be1c6c9051182d299404b0186d5 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Wed, 21 Feb 2024 03:23:07 -0800 Subject: [PATCH 001/116] Add support for multiple comments per move --- chess/pgn.py | 108 +++++++++++++++++++++++++++++---------------------- test.py | 58 +++++++++++++-------------- 2 files changed, 91 insertions(+), 75 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index 55eddbc29..65ef0e6d5 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -197,24 +197,24 @@ class GameNode(abc.ABC): variations: List[ChildNode] """A list of child nodes.""" - comment: str + comments: list[str] """ - A comment that goes behind the move leading to this node. Comments + Comments that go behind the move leading to this node. Comments that occur before any moves are assigned to the root node. """ - starting_comment: str + starting_comments: list[str] nags: Set[int] - def __init__(self, *, comment: str = "") -> None: + def __init__(self, *, comment: Union[str, list[str]] = "") -> None: self.parent = None self.move = None self.variations = [] - self.comment = comment + self.comment = comment if isinstance(comment, list) else [comment] if comment else [] # Deprecated: These should be properties of ChildNode, but need to # remain here for backwards compatibility. - self.starting_comment = "" + self.starting_comment: list[str] = [] self.nags = set() @abc.abstractmethod @@ -386,7 +386,7 @@ def remove_variation(self, move: Union[int, chess.Move, GameNode]) -> None: """Removes a variation.""" self.variations.remove(self.variation(move)) - def add_variation(self, move: chess.Move, *, comment: str = "", starting_comment: str = "", nags: Iterable[int] = []) -> ChildNode: + def add_variation(self, move: chess.Move, *, comment: Union[str, list[str]] = "", starting_comment: Union[str, list[str]] = "", nags: Iterable[int] = []) -> ChildNode: """Creates a child node with the given attributes.""" # Instanciate ChildNode only in this method. return ChildNode(self, move, comment=comment, starting_comment=starting_comment, nags=nags) @@ -417,7 +417,7 @@ def mainline_moves(self) -> Mainline[chess.Move]: """Returns an iterable over the main moves after this node.""" return Mainline(self, lambda node: node.move) - def add_line(self, moves: Iterable[chess.Move], *, comment: str = "", starting_comment: str = "", nags: Iterable[int] = []) -> GameNode: + def add_line(self, moves: Iterable[chess.Move], *, comment: Union[str, list[str]] = "", starting_comment: Union[str, list[str]] = "", nags: Iterable[int] = []) -> GameNode: """ Creates a sequence of child nodes for the given list of moves. Adds *comment* and *nags* to the last node of the line and returns it. @@ -431,9 +431,12 @@ def add_line(self, moves: Iterable[chess.Move], *, comment: str = "", starting_c # Merge comment and NAGs. if node.comment: - node.comment += " " + comment + if isinstance(comment, str): + node.comment.append(comment) + else: + node.comment.extend(comment) else: - node.comment = comment + node.comment = comment if isinstance(comment, list) else [comment] if comment else [] node.nags.update(nags) @@ -446,7 +449,7 @@ def eval(self) -> Optional[chess.engine.PovScore]: Complexity is `O(n)`. """ - match = EVAL_REGEX.search(self.comment) + match = EVAL_REGEX.search(" ".join(self.comment)) if not match: return None @@ -472,7 +475,7 @@ def eval_depth(self) -> Optional[int]: Complexity is `O(1)`. """ - match = EVAL_REGEX.search(self.comment) + match = EVAL_REGEX.search(" ".join(self.comment)) return int(match.group("depth")) if match and match.group("depth") else None def set_eval(self, score: Optional[chess.engine.PovScore], depth: Optional[int] = None) -> None: @@ -489,12 +492,16 @@ def set_eval(self, score: Optional[chess.engine.PovScore], depth: Optional[int] elif score.white().mate(): eval = f"[%eval #{score.white().mate()}{depth_suffix}]" - self.comment, found = EVAL_REGEX.subn(_condense_affix(eval), self.comment, count=1) + found = 0 + for index in range(len(self.comment)): + self.comment[index], found = EVAL_REGEX.subn(_condense_affix(eval), self.comment[index], count=1) + if found: + break + + self.comment = list(filter(None, self.comment)) if not found and eval: - if self.comment and not self.comment.endswith(" "): - self.comment += " " - self.comment += eval + self.comment.append(eval) def arrows(self) -> List[chess.svg.Arrow]: """ @@ -504,7 +511,7 @@ def arrows(self) -> List[chess.svg.Arrow]: Returns a list of :class:`arrows `. """ arrows = [] - for match in ARROWS_REGEX.finditer(self.comment): + for match in ARROWS_REGEX.finditer(" ".join(self.comment)): for group in match.group("arrows").split(","): arrows.append(chess.svg.Arrow.from_pgn(group)) @@ -526,7 +533,10 @@ def set_arrows(self, arrows: Iterable[Union[chess.svg.Arrow, Tuple[Square, Squar pass (csl if arrow.tail == arrow.head else cal).append(arrow.pgn()) # type: ignore - self.comment = ARROWS_REGEX.sub(_condense_affix(""), self.comment) + for index in range(len(self.comment)): + self.comment[index] = ARROWS_REGEX.sub(_condense_affix(""), self.comment[index]) + + self.comment = list(filter(None, self.comment)) prefix = "" if csl: @@ -534,10 +544,8 @@ def set_arrows(self, arrows: Iterable[Union[chess.svg.Arrow, Tuple[Square, Squar if cal: prefix += f"[%cal {','.join(cal)}]" - if prefix and self.comment and not self.comment.startswith(" ") and not self.comment.startswith("\n"): - self.comment = prefix + " " + self.comment - else: - self.comment = prefix + self.comment + if prefix: + self.comment.insert(0, prefix) def clock(self) -> Optional[float]: """ @@ -547,7 +555,7 @@ def clock(self) -> Optional[float]: Returns the player's remaining time to the next time control after this move, in seconds. """ - match = CLOCK_REGEX.search(self.comment) + match = CLOCK_REGEX.search(" ".join(self.comment)) if match is None: return None return int(match.group("hours")) * 3600 + int(match.group("minutes")) * 60 + float(match.group("seconds")) @@ -566,12 +574,16 @@ def set_clock(self, seconds: Optional[float]) -> None: seconds_part = f"{seconds:06.3f}".rstrip("0").rstrip(".") clk = f"[%clk {hours:d}:{minutes:02d}:{seconds_part}]" - self.comment, found = CLOCK_REGEX.subn(_condense_affix(clk), self.comment, count=1) + found = 0 + for index in range(len(self.comment)): + self.comment[index], found = CLOCK_REGEX.subn(_condense_affix(clk), self.comment[index], count=1) + if found: + break + + self.comment = list(filter(None, self.comment)) if not found and clk: - if self.comment and not self.comment.endswith(" ") and not self.comment.endswith("\n"): - self.comment += " " - self.comment += clk + self.comment.append(clk) def emt(self) -> Optional[float]: """ @@ -581,7 +593,7 @@ def emt(self) -> Optional[float]: Returns the player's elapsed move time use for the comment of this move, in seconds. """ - match = EMT_REGEX.search(self.comment) + match = EMT_REGEX.search(" ".join(self.comment)) if match is None: return None return int(match.group("hours")) * 3600 + int(match.group("minutes")) * 60 + float(match.group("seconds")) @@ -600,12 +612,16 @@ def set_emt(self, seconds: Optional[float]) -> None: seconds_part = f"{seconds:06.3f}".rstrip("0").rstrip(".") emt = f"[%emt {hours:d}:{minutes:02d}:{seconds_part}]" - self.comment, found = EMT_REGEX.subn(_condense_affix(emt), self.comment, count=1) + found = 0 + for index in range(len(self.comment)): + self.comment[index], found = EMT_REGEX.subn(_condense_affix(emt), self.comment[index], count=1) + if found: + break + + self.comment = list(filter(None, self.comment)) if not found and emt: - if self.comment and not self.comment.endswith(" ") and not self.comment.endswith("\n"): - self.comment += " " - self.comment += emt + self.comment.append(emt) @abc.abstractmethod def accept(self, visitor: BaseVisitor[ResultT]) -> ResultT: @@ -661,7 +677,7 @@ class ChildNode(GameNode): move: chess.Move """The move leading to this node.""" - starting_comment: str + starting_comment: list[str] """ A comment for the start of a variation. Only nodes that actually start a variation (:func:`~chess.pgn.GameNode.starts_variation()` @@ -675,14 +691,14 @@ class ChildNode(GameNode): node of the game will never have NAGs. """ - def __init__(self, parent: GameNode, move: chess.Move, *, comment: str = "", starting_comment: str = "", nags: Iterable[int] = []) -> None: + def __init__(self, parent: GameNode, move: chess.Move, *, comment: Union[str, list[str]] = "", starting_comment: Union[str, list[str]] = "", nags: Iterable[int] = []) -> None: super().__init__(comment=comment) self.parent = parent self.move = move self.parent.variations.append(self) self.nags.update(nags) - self.starting_comment = starting_comment + self.starting_comment = starting_comment if isinstance(starting_comment, list) else [starting_comment] if starting_comment else [] def board(self) -> chess.Board: stack: List[chess.Move] = [] @@ -1134,7 +1150,7 @@ def visit_board(self, board: chess.Board) -> None: """ pass - def visit_comment(self, comment: str) -> None: + def visit_comment(self, comment: list[str]) -> None: """Called for each comment.""" pass @@ -1188,7 +1204,7 @@ def begin_game(self) -> None: self.game: GameT = self.Game() self.variation_stack: List[GameNode] = [self.game] - self.starting_comment = "" + self.starting_comment: list[str] = [] self.in_variation = False def begin_headers(self) -> Headers: @@ -1213,22 +1229,22 @@ def visit_result(self, result: str) -> None: if self.game.headers.get("Result", "*") == "*": self.game.headers["Result"] = result - def visit_comment(self, comment: str) -> None: + def visit_comment(self, comment: list[str]) -> None: if self.in_variation or (self.variation_stack[-1].parent is None and self.variation_stack[-1].is_end()): # Add as a comment for the current node if in the middle of # a variation. Add as a comment for the game if the comment # starts before any move. - new_comment = [self.variation_stack[-1].comment, comment] - self.variation_stack[-1].comment = " ".join(filter(None, new_comment)) + new_comment = self.variation_stack[-1].comment + comment + self.variation_stack[-1].comment = list(filter(None, new_comment)) else: # Otherwise, it is a starting comment. - new_comment = [self.starting_comment, comment] - self.starting_comment = " ".join(filter(None, new_comment)) + new_comment = self.starting_comment + comment + self.starting_comment = list(filter(None, new_comment)) def visit_move(self, board: chess.Board, move: chess.Move) -> None: self.variation_stack[-1] = self.variation_stack[-1].add_variation(move) self.variation_stack[-1].starting_comment = self.starting_comment - self.starting_comment = "" + self.starting_comment = [] self.in_variation = True def handle_error(self, error: Exception) -> None: @@ -1396,9 +1412,9 @@ def end_variation(self) -> None: self.write_token(") ") self.force_movenumber = True - def visit_comment(self, comment: str) -> None: + def visit_comment(self, comment: list[str]) -> None: if self.comments and (self.variations or not self.variation_depth): - self.write_token("{ " + comment.replace("}", "").strip() + " } ") + self.write_token(" ".join("{ " + single_comment.replace("}", "").strip() + " }" for single_comment in comment) + " ") self.force_movenumber = True def visit_nag(self, nag: int) -> None: @@ -1693,7 +1709,7 @@ def read_game(handle: TextIO, *, Visitor: Any = GameBuilder) -> Any: line = line[close_index + 1:] if not skip_variation_depth: - visitor.visit_comment("".join(comment_lines)) + visitor.visit_comment(["".join(comment_lines)]) # Continue with the current line. fresh_line = False diff --git a/test.py b/test.py index e1d0d50c5..d3961cc6b 100755 --- a/test.py +++ b/test.py @@ -2078,29 +2078,29 @@ class PgnTestCase(unittest.TestCase): def test_exporter(self): game = chess.pgn.Game() - game.comment = "Test game:" + game.comment = ["Test game:"] game.headers["Result"] = "*" game.headers["VeryLongHeader"] = "This is a very long header, much wider than the 80 columns that PGNs are formatted with by default" e4 = game.add_variation(game.board().parse_san("e4")) - e4.comment = "Scandinavian Defense:" + e4.comment = ["Scandinavian Defense:"] e4_d5 = e4.add_variation(e4.board().parse_san("d5")) e4_h5 = e4.add_variation(e4.board().parse_san("h5")) e4_h5.nags.add(chess.pgn.NAG_MISTAKE) - e4_h5.starting_comment = "This" - e4_h5.comment = "is nonsense" + e4_h5.starting_comment = ["This"] + e4_h5.comment = ["is nonsense"] e4_e5 = e4.add_variation(e4.board().parse_san("e5")) e4_e5_Qf3 = e4_e5.add_variation(e4_e5.board().parse_san("Qf3")) e4_e5_Qf3.nags.add(chess.pgn.NAG_MISTAKE) e4_c5 = e4.add_variation(e4.board().parse_san("c5")) - e4_c5.comment = "Sicilian" + e4_c5.comment = ["Sicilian"] e4_d5_exd5 = e4_d5.add_main_variation(e4_d5.board().parse_san("exd5")) - e4_d5_exd5.comment = "Best" + e4_d5_exd5.comment = ["Best"] # Test string exporter with various options. exporter = chess.pgn.StringExporter(headers=False, comments=False, variations=False) @@ -2229,7 +2229,7 @@ def test_comment_at_eol(self): # Make sure the comment for the second variation is there. self.assertIn(5, node[1].nags) - self.assertEqual(node[1].comment, "\n/\\ Ne7, c6") + self.assertEqual(node[1].comment, ["\n/\\ Ne7, c6"]) def test_promotion_without_equals(self): # Example game from https://github.com/rozim/ChessData as originally @@ -2317,12 +2317,12 @@ def test_variation_stack(self): def test_game_starting_comment(self): pgn = io.StringIO("{ Game starting comment } 1. d3") game = chess.pgn.read_game(pgn) - self.assertEqual(game.comment, "Game starting comment") + self.assertEqual(game.comment, ["Game starting comment"]) self.assertEqual(game[0].san(), "d3") pgn = io.StringIO("{ Empty game, but has a comment }") game = chess.pgn.read_game(pgn) - self.assertEqual(game.comment, "Empty game, but has a comment") + self.assertEqual(game.comment, ["Empty game, but has a comment"]) def test_game_starting_variation(self): pgn = io.StringIO(textwrap.dedent("""\ @@ -2330,7 +2330,7 @@ def test_game_starting_variation(self): """)) game = chess.pgn.read_game(pgn) - self.assertEqual(game.comment, "Start of game") + self.assertEqual(game.comment, ["Start of game"]) node = game[0] self.assertEqual(node.move, chess.Move.from_uci("e2e4")) @@ -2340,7 +2340,7 @@ def test_game_starting_variation(self): node = game[1] self.assertEqual(node.move, chess.Move.from_uci("d2d4")) self.assertFalse(node.comment) - self.assertEqual(node.starting_comment, "Start of variation") + self.assertEqual(node.starting_comment, ["Start of variation"]) def test_annotation_symbols(self): pgn = io.StringIO("1. b4?! g6 2. Bb2 Nc6? 3. Bxh8!!") @@ -2685,12 +2685,12 @@ def test_add_line(self): tail = game.add_line(moves, starting_comment="start", comment="end", nags=(17, 42)) self.assertEqual(tail.parent.move, chess.Move.from_uci("g1f3")) - self.assertEqual(tail.parent.starting_comment, "start") - self.assertEqual(tail.parent.comment, "") + self.assertEqual(tail.parent.starting_comment, ["start"]) + self.assertEqual(tail.parent.comment, []) self.assertEqual(len(tail.parent.nags), 0) self.assertEqual(tail.move, chess.Move.from_uci("d7d5")) - self.assertEqual(tail.comment, "end") + self.assertEqual(tail.comment, ["end"]) self.assertIn(42, tail.nags) def test_mainline(self): @@ -2820,27 +2820,27 @@ def test_recursion(self): def test_annotations(self): game = chess.pgn.Game() - game.comment = "foo [%bar] baz" + game.comment = ["foo [%bar] baz"] self.assertTrue(game.clock() is None) clock = 12345 game.set_clock(clock) - self.assertEqual(game.comment, "foo [%bar] baz [%clk 3:25:45]") + self.assertEqual(game.comment, ["foo [%bar] baz", "[%clk 3:25:45]"]) self.assertEqual(game.clock(), clock) self.assertTrue(game.eval() is None) game.set_eval(chess.engine.PovScore(chess.engine.Cp(-80), chess.WHITE)) - self.assertEqual(game.comment, "foo [%bar] baz [%clk 3:25:45] [%eval -0.80]") + self.assertEqual(game.comment, ["foo [%bar] baz", "[%clk 3:25:45]", "[%eval -0.80]"]) self.assertEqual(game.eval().white().score(), -80) self.assertEqual(game.eval_depth(), None) game.set_eval(chess.engine.PovScore(chess.engine.Mate(1), chess.WHITE), 5) - self.assertEqual(game.comment, "foo [%bar] baz [%clk 3:25:45] [%eval #1,5]") + self.assertEqual(game.comment, ["foo [%bar] baz", "[%clk 3:25:45]", "[%eval #1,5]"]) self.assertEqual(game.eval().white().mate(), 1) self.assertEqual(game.eval_depth(), 5) self.assertEqual(game.arrows(), []) game.set_arrows([(chess.A1, chess.A1), chess.svg.Arrow(chess.A1, chess.H1, color="red"), chess.svg.Arrow(chess.B1, chess.B8)]) - self.assertEqual(game.comment, "[%csl Ga1][%cal Ra1h1,Gb1b8] foo [%bar] baz [%clk 3:25:45] [%eval #1,5]") + self.assertEqual(game.comment, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]", "[%eval #1,5]"]) arrows = game.arrows() self.assertEqual(len(arrows), 3) self.assertEqual(arrows[0].color, "green") @@ -2850,18 +2850,18 @@ def test_annotations(self): self.assertTrue(game.emt() is None) emt = 321 game.set_emt(emt) - self.assertEqual(game.comment, "[%csl Ga1][%cal Ra1h1,Gb1b8] foo [%bar] baz [%clk 3:25:45] [%eval #1,5] [%emt 0:05:21]") + self.assertEqual(game.comment, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]", "[%eval #1,5]", "[%emt 0:05:21]"]) self.assertEqual(game.emt(), emt) game.set_eval(None) - self.assertEqual(game.comment, "[%csl Ga1][%cal Ra1h1,Gb1b8] foo [%bar] baz [%clk 3:25:45] [%emt 0:05:21]") + self.assertEqual(game.comment, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]", "[%emt 0:05:21]"]) game.set_emt(None) - self.assertEqual(game.comment, "[%csl Ga1][%cal Ra1h1,Gb1b8] foo [%bar] baz [%clk 3:25:45]") + self.assertEqual(game.comment, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]"]) game.set_clock(None) game.set_arrows([]) - self.assertEqual(game.comment, "foo [%bar] baz") + self.assertEqual(game.comment, ["foo [%bar] baz"]) def test_eval(self): game = chess.pgn.Game() @@ -2871,28 +2871,28 @@ def test_eval(self): def test_float_emt(self): game = chess.pgn.Game() - game.comment = "[%emt 0:00:01.234]" + game.comment = ["[%emt 0:00:01.234]"] self.assertEqual(game.emt(), 1.234) game.set_emt(6.54321) - self.assertEqual(game.comment, "[%emt 0:00:06.543]") + self.assertEqual(game.comment, ["[%emt 0:00:06.543]"]) self.assertEqual(game.emt(), 6.543) game.set_emt(-70) - self.assertEqual(game.comment, "[%emt 0:00:00]") # Clamped + self.assertEqual(game.comment, ["[%emt 0:00:00]"]) # Clamped self.assertEqual(game.emt(), 0) def test_float_clk(self): game = chess.pgn.Game() - game.comment = "[%clk 0:00:01.234]" + game.comment = ["[%clk 0:00:01.234]"] self.assertEqual(game.clock(), 1.234) game.set_clock(6.54321) - self.assertEqual(game.comment, "[%clk 0:00:06.543]") + self.assertEqual(game.comment, ["[%clk 0:00:06.543]"]) self.assertEqual(game.clock(), 6.543) game.set_clock(-70) - self.assertEqual(game.comment, "[%clk 0:00:00]") # Clamped + self.assertEqual(game.comment, ["[%clk 0:00:00]"]) # Clamped self.assertEqual(game.clock(), 0) def test_node_turn(self): From 2269d6a05e137dfe42043474fdaca58c2c6b84a3 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Wed, 21 Feb 2024 03:25:33 -0800 Subject: [PATCH 002/116] Add test for exporting a move with multiple comments --- test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test.py b/test.py index d3961cc6b..2008ef49d 100755 --- a/test.py +++ b/test.py @@ -2100,7 +2100,7 @@ def test_exporter(self): e4_c5.comment = ["Sicilian"] e4_d5_exd5 = e4_d5.add_main_variation(e4_d5.board().parse_san("exd5")) - e4_d5_exd5.comment = ["Best"] + e4_d5_exd5.comment = ["Best", "and the end of this example"] # Test string exporter with various options. exporter = chess.pgn.StringExporter(headers=False, comments=False, variations=False) @@ -2125,7 +2125,7 @@ def test_exporter(self): { Test game: } 1. e4 { Scandinavian Defense: } 1... d5 ( { This } 1... h5 $2 { is nonsense } ) ( 1... e5 2. Qf3 $2 ) ( 1... c5 { Sicilian } ) 2. exd5 - { Best } *""") + { Best } { and the end of this example } *""") self.assertEqual(str(exporter), pgn) # Test file exporter. From 05da5981c5494b1c87b214a0fd46dfc4b0f6ac5a Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Wed, 21 Feb 2024 03:48:21 -0800 Subject: [PATCH 003/116] Undo member renaming --- chess/pgn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index 65ef0e6d5..f13812bae 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -197,13 +197,13 @@ class GameNode(abc.ABC): variations: List[ChildNode] """A list of child nodes.""" - comments: list[str] + comment: list[str] """ Comments that go behind the move leading to this node. Comments that occur before any moves are assigned to the root node. """ - starting_comments: list[str] + starting_comment: list[str] nags: Set[int] def __init__(self, *, comment: Union[str, list[str]] = "") -> None: From c06030895cbcf49251279724bd19669c6f580349 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Wed, 21 Feb 2024 03:48:45 -0800 Subject: [PATCH 004/116] Add test for reading game with mulit-comment moves --- test.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test.py b/test.py index 2008ef49d..c912948c4 100755 --- a/test.py +++ b/test.py @@ -2215,6 +2215,14 @@ def test_read_game(self): self.assertEqual(sixth_game.headers["White"], "Deep Blue (Computer)") self.assertEqual(sixth_game.headers["Result"], "1-0") + def test_read_game_with_multicomment_move(self): + pgn = io.StringIO("1. e4 {A common opening} 1... e5 {A common response} {An uncommon comment}") + game = chess.pgn.read_game(pgn) + first_move = game.variation(0) + self.assertEqual(first_move.comment, ["A common opening"]) + second_move = first_move.variation(0) + self.assertEqual(second_move.comment, ["A common response", "An uncommon comment"]) + def test_comment_at_eol(self): pgn = io.StringIO(textwrap.dedent("""\ 1. e4 e5 2. Nf3 Nc6 3. Bc4 Bc5 4. c3 Nf6 5. d3 d6 6. Nbd2 a6 $6 (6... Bb6 $5 { From 8d076cb77600d84f692e19f70abfdf9d04b93498 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Thu, 22 Feb 2024 03:43:33 -0800 Subject: [PATCH 005/116] Undo comment change --- chess/pgn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chess/pgn.py b/chess/pgn.py index f13812bae..513698dff 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -199,7 +199,7 @@ class GameNode(abc.ABC): comment: list[str] """ - Comments that go behind the move leading to this node. Comments + A comment that goes behind the move leading to this node. Comments that occur before any moves are assigned to the root node. """ From 1df1b76fb064765b7e7510fbe6b35c1b27c3dde0 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Thu, 22 Feb 2024 03:55:05 -0800 Subject: [PATCH 006/116] Create class to hold multiple comments --- chess/pgn.py | 116 ++++++++++++++++++++++++++++++++++++++++----------- test.py | 18 ++++---- 2 files changed, 100 insertions(+), 34 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index 513698dff..763b90f02 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -184,6 +184,70 @@ def __init__(self, node: ChildNode, *, is_variation: bool = False, sidelines: bo self.in_variation = False +class GameNodeComment: + """ + PGN Comment Storage + + A class that can hold one or more comments for a GameNode. + """ + def __init__(self, comment: Union[str, list[str]] = ""): + self.set(comment) + + def set(self, new_comment: Union[str, list[str]]): + """Replace the comment with a new comment or a list of comments.""" + self._comments = new_comment if isinstance(new_comment, list) else [new_comment] if new_comment else [] + + def pgn_format(self) -> str: + """Create a string representation of the comments in PGN format.""" + comments = list(map(lambda s: s.replace("{", ""), self._comments)) + comments = list(map(lambda s: s.replace("}", "").strip(), comments)) + output_comments = GameNodeComment(comments) + output_comments.remove_empty() + return "{ " + output_comments.join(" } { ") + " }" + + def remove_empty(self) -> None: + """Remove empty comments from the comment list.""" + self._comments = list(filter(None, self._comments)) + + def append(self, new_comment: str) -> None: + """Append a new comment to the end of the comment list.""" + self._comments.append(new_comment) + + def extend(self, new_comments: list[str]) -> None: + """Append several new comments to the end of the comment list.""" + self._comments.extend(new_comments) + + def insert(self, index: int, new_comment: str) -> None: + """Insert a new comment before the specified index.""" + self._comments.insert(index, new_comment) + + def join(self, joiner: str) -> str: + """Join all of the comments together with a joiner string between each.""" + return joiner.join(self._comments) + + def __len__(self) -> int: + return len(self._comments) + + def __getitem__(self, index: int) -> str: + return self._comments[index] + + def __setitem__(self, index: int, new_comment: str) -> None: + self._comments[index] = new_comment + + def __add__(self, other: GameNodeComment) -> GameNodeComment: + return GameNodeComment(self._comments + other._comments) + + def __eq__(self, other: object) -> bool: + if isinstance(other, str): + return len(self) == 1 and self[0] == other + elif isinstance(other, list): + return self._comments == other + elif isinstance(other, GameNodeComment): + return self._comments == other._comments + else: + return False + + class GameNode(abc.ABC): parent: Optional[GameNode] """The parent node or ``None`` if this is the root node of the game.""" @@ -197,24 +261,24 @@ class GameNode(abc.ABC): variations: List[ChildNode] """A list of child nodes.""" - comment: list[str] + comment: GameNodeComment """ A comment that goes behind the move leading to this node. Comments that occur before any moves are assigned to the root node. """ - starting_comment: list[str] + starting_comment: GameNodeComment nags: Set[int] def __init__(self, *, comment: Union[str, list[str]] = "") -> None: self.parent = None self.move = None self.variations = [] - self.comment = comment if isinstance(comment, list) else [comment] if comment else [] + self.comment = GameNodeComment(comment) # Deprecated: These should be properties of ChildNode, but need to # remain here for backwards compatibility. - self.starting_comment: list[str] = [] + self.starting_comment = GameNodeComment() self.nags = set() @abc.abstractmethod @@ -436,7 +500,7 @@ def add_line(self, moves: Iterable[chess.Move], *, comment: Union[str, list[str] else: node.comment.extend(comment) else: - node.comment = comment if isinstance(comment, list) else [comment] if comment else [] + node.comment.set(comment) node.nags.update(nags) @@ -449,7 +513,7 @@ def eval(self) -> Optional[chess.engine.PovScore]: Complexity is `O(n)`. """ - match = EVAL_REGEX.search(" ".join(self.comment)) + match = EVAL_REGEX.search(self.comment.join(" ")) if not match: return None @@ -475,7 +539,7 @@ def eval_depth(self) -> Optional[int]: Complexity is `O(1)`. """ - match = EVAL_REGEX.search(" ".join(self.comment)) + match = EVAL_REGEX.search(self.comment.join(" ")) return int(match.group("depth")) if match and match.group("depth") else None def set_eval(self, score: Optional[chess.engine.PovScore], depth: Optional[int] = None) -> None: @@ -498,7 +562,7 @@ def set_eval(self, score: Optional[chess.engine.PovScore], depth: Optional[int] if found: break - self.comment = list(filter(None, self.comment)) + self.comment.remove_empty() if not found and eval: self.comment.append(eval) @@ -511,7 +575,7 @@ def arrows(self) -> List[chess.svg.Arrow]: Returns a list of :class:`arrows `. """ arrows = [] - for match in ARROWS_REGEX.finditer(" ".join(self.comment)): + for match in ARROWS_REGEX.finditer(self.comment.join(" ")): for group in match.group("arrows").split(","): arrows.append(chess.svg.Arrow.from_pgn(group)) @@ -536,7 +600,7 @@ def set_arrows(self, arrows: Iterable[Union[chess.svg.Arrow, Tuple[Square, Squar for index in range(len(self.comment)): self.comment[index] = ARROWS_REGEX.sub(_condense_affix(""), self.comment[index]) - self.comment = list(filter(None, self.comment)) + self.comment.remove_empty() prefix = "" if csl: @@ -555,7 +619,7 @@ def clock(self) -> Optional[float]: Returns the player's remaining time to the next time control after this move, in seconds. """ - match = CLOCK_REGEX.search(" ".join(self.comment)) + match = CLOCK_REGEX.search(self.comment.join(" ")) if match is None: return None return int(match.group("hours")) * 3600 + int(match.group("minutes")) * 60 + float(match.group("seconds")) @@ -580,7 +644,7 @@ def set_clock(self, seconds: Optional[float]) -> None: if found: break - self.comment = list(filter(None, self.comment)) + self.comment.remove_empty() if not found and clk: self.comment.append(clk) @@ -593,7 +657,7 @@ def emt(self) -> Optional[float]: Returns the player's elapsed move time use for the comment of this move, in seconds. """ - match = EMT_REGEX.search(" ".join(self.comment)) + match = EMT_REGEX.search(self.comment.join(" ")) if match is None: return None return int(match.group("hours")) * 3600 + int(match.group("minutes")) * 60 + float(match.group("seconds")) @@ -618,7 +682,7 @@ def set_emt(self, seconds: Optional[float]) -> None: if found: break - self.comment = list(filter(None, self.comment)) + self.comment.remove_empty() if not found and emt: self.comment.append(emt) @@ -677,7 +741,7 @@ class ChildNode(GameNode): move: chess.Move """The move leading to this node.""" - starting_comment: list[str] + starting_comment: GameNodeComment """ A comment for the start of a variation. Only nodes that actually start a variation (:func:`~chess.pgn.GameNode.starts_variation()` @@ -698,7 +762,7 @@ def __init__(self, parent: GameNode, move: chess.Move, *, comment: Union[str, li self.parent.variations.append(self) self.nags.update(nags) - self.starting_comment = starting_comment if isinstance(starting_comment, list) else [starting_comment] if starting_comment else [] + self.starting_comment = GameNodeComment(starting_comment) def board(self) -> chess.Board: stack: List[chess.Move] = [] @@ -1150,7 +1214,7 @@ def visit_board(self, board: chess.Board) -> None: """ pass - def visit_comment(self, comment: list[str]) -> None: + def visit_comment(self, comment: GameNodeComment) -> None: """Called for each comment.""" pass @@ -1204,7 +1268,7 @@ def begin_game(self) -> None: self.game: GameT = self.Game() self.variation_stack: List[GameNode] = [self.game] - self.starting_comment: list[str] = [] + self.starting_comment = GameNodeComment() self.in_variation = False def begin_headers(self) -> Headers: @@ -1229,22 +1293,24 @@ def visit_result(self, result: str) -> None: if self.game.headers.get("Result", "*") == "*": self.game.headers["Result"] = result - def visit_comment(self, comment: list[str]) -> None: + def visit_comment(self, comment: GameNodeComment) -> None: if self.in_variation or (self.variation_stack[-1].parent is None and self.variation_stack[-1].is_end()): # Add as a comment for the current node if in the middle of # a variation. Add as a comment for the game if the comment # starts before any move. new_comment = self.variation_stack[-1].comment + comment - self.variation_stack[-1].comment = list(filter(None, new_comment)) + new_comment.remove_empty() + self.variation_stack[-1].comment = new_comment else: # Otherwise, it is a starting comment. new_comment = self.starting_comment + comment - self.starting_comment = list(filter(None, new_comment)) + new_comment.remove_empty() + self.starting_comment = new_comment def visit_move(self, board: chess.Board, move: chess.Move) -> None: self.variation_stack[-1] = self.variation_stack[-1].add_variation(move) self.variation_stack[-1].starting_comment = self.starting_comment - self.starting_comment = [] + self.starting_comment = GameNodeComment() self.in_variation = True def handle_error(self, error: Exception) -> None: @@ -1412,9 +1478,9 @@ def end_variation(self) -> None: self.write_token(") ") self.force_movenumber = True - def visit_comment(self, comment: list[str]) -> None: + def visit_comment(self, comment: GameNodeComment) -> None: if self.comments and (self.variations or not self.variation_depth): - self.write_token(" ".join("{ " + single_comment.replace("}", "").strip() + " }" for single_comment in comment) + " ") + self.write_token(comment.pgn_format() + " ") self.force_movenumber = True def visit_nag(self, nag: int) -> None: @@ -1709,7 +1775,7 @@ def read_game(handle: TextIO, *, Visitor: Any = GameBuilder) -> Any: line = line[close_index + 1:] if not skip_variation_depth: - visitor.visit_comment(["".join(comment_lines)]) + visitor.visit_comment(GameNodeComment("".join(comment_lines))) # Continue with the current line. fresh_line = False diff --git a/test.py b/test.py index c912948c4..8750031ca 100755 --- a/test.py +++ b/test.py @@ -2078,29 +2078,29 @@ class PgnTestCase(unittest.TestCase): def test_exporter(self): game = chess.pgn.Game() - game.comment = ["Test game:"] + game.comment.set("Test game:") game.headers["Result"] = "*" game.headers["VeryLongHeader"] = "This is a very long header, much wider than the 80 columns that PGNs are formatted with by default" e4 = game.add_variation(game.board().parse_san("e4")) - e4.comment = ["Scandinavian Defense:"] + e4.comment.set("Scandinavian Defense:") e4_d5 = e4.add_variation(e4.board().parse_san("d5")) e4_h5 = e4.add_variation(e4.board().parse_san("h5")) e4_h5.nags.add(chess.pgn.NAG_MISTAKE) - e4_h5.starting_comment = ["This"] - e4_h5.comment = ["is nonsense"] + e4_h5.starting_comment.set("This") + e4_h5.comment.set("is nonsense") e4_e5 = e4.add_variation(e4.board().parse_san("e5")) e4_e5_Qf3 = e4_e5.add_variation(e4_e5.board().parse_san("Qf3")) e4_e5_Qf3.nags.add(chess.pgn.NAG_MISTAKE) e4_c5 = e4.add_variation(e4.board().parse_san("c5")) - e4_c5.comment = ["Sicilian"] + e4_c5.comment.set("Sicilian") e4_d5_exd5 = e4_d5.add_main_variation(e4_d5.board().parse_san("exd5")) - e4_d5_exd5.comment = ["Best", "and the end of this example"] + e4_d5_exd5.comment.set(["Best", "and the end of this example"]) # Test string exporter with various options. exporter = chess.pgn.StringExporter(headers=False, comments=False, variations=False) @@ -2828,7 +2828,7 @@ def test_recursion(self): def test_annotations(self): game = chess.pgn.Game() - game.comment = ["foo [%bar] baz"] + game.comment = chess.pgn.GameNodeComment("foo [%bar] baz") self.assertTrue(game.clock() is None) clock = 12345 @@ -2879,7 +2879,7 @@ def test_eval(self): def test_float_emt(self): game = chess.pgn.Game() - game.comment = ["[%emt 0:00:01.234]"] + game.comment = chess.pgn.GameNodeComment("[%emt 0:00:01.234]") self.assertEqual(game.emt(), 1.234) game.set_emt(6.54321) @@ -2892,7 +2892,7 @@ def test_float_emt(self): def test_float_clk(self): game = chess.pgn.Game() - game.comment = ["[%clk 0:00:01.234]"] + game.comment.set("[%clk 0:00:01.234]") self.assertEqual(game.clock(), 1.234) game.set_clock(6.54321) From 829a3bf6ded7f8d738655ee924df90812b8af0c2 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Thu, 22 Feb 2024 04:01:57 -0800 Subject: [PATCH 007/116] Empty GameNodeComments equal empty strings --- chess/pgn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chess/pgn.py b/chess/pgn.py index 763b90f02..058c932a8 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -239,7 +239,7 @@ def __add__(self, other: GameNodeComment) -> GameNodeComment: def __eq__(self, other: object) -> bool: if isinstance(other, str): - return len(self) == 1 and self[0] == other + return (len(self) == 1 and self[0] == other) or (not self and not other) elif isinstance(other, list): return self._comments == other elif isinstance(other, GameNodeComment): From 7e0081e6d5b5f1188224a02ab9d78f0fe6b5b80d Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Thu, 22 Feb 2024 04:02:15 -0800 Subject: [PATCH 008/116] Revert unnecessary test changes --- test.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test.py b/test.py index 8750031ca..f6d30a4d1 100755 --- a/test.py +++ b/test.py @@ -2219,7 +2219,7 @@ def test_read_game_with_multicomment_move(self): pgn = io.StringIO("1. e4 {A common opening} 1... e5 {A common response} {An uncommon comment}") game = chess.pgn.read_game(pgn) first_move = game.variation(0) - self.assertEqual(first_move.comment, ["A common opening"]) + self.assertEqual(first_move.comment, "A common opening") second_move = first_move.variation(0) self.assertEqual(second_move.comment, ["A common response", "An uncommon comment"]) @@ -2237,7 +2237,7 @@ def test_comment_at_eol(self): # Make sure the comment for the second variation is there. self.assertIn(5, node[1].nags) - self.assertEqual(node[1].comment, ["\n/\\ Ne7, c6"]) + self.assertEqual(node[1].comment, "\n/\\ Ne7, c6") def test_promotion_without_equals(self): # Example game from https://github.com/rozim/ChessData as originally @@ -2325,12 +2325,12 @@ def test_variation_stack(self): def test_game_starting_comment(self): pgn = io.StringIO("{ Game starting comment } 1. d3") game = chess.pgn.read_game(pgn) - self.assertEqual(game.comment, ["Game starting comment"]) + self.assertEqual(game.comment, "Game starting comment") self.assertEqual(game[0].san(), "d3") pgn = io.StringIO("{ Empty game, but has a comment }") game = chess.pgn.read_game(pgn) - self.assertEqual(game.comment, ["Empty game, but has a comment"]) + self.assertEqual(game.comment, "Empty game, but has a comment") def test_game_starting_variation(self): pgn = io.StringIO(textwrap.dedent("""\ @@ -2338,7 +2338,7 @@ def test_game_starting_variation(self): """)) game = chess.pgn.read_game(pgn) - self.assertEqual(game.comment, ["Start of game"]) + self.assertEqual(game.comment, "Start of game") node = game[0] self.assertEqual(node.move, chess.Move.from_uci("e2e4")) @@ -2348,7 +2348,7 @@ def test_game_starting_variation(self): node = game[1] self.assertEqual(node.move, chess.Move.from_uci("d2d4")) self.assertFalse(node.comment) - self.assertEqual(node.starting_comment, ["Start of variation"]) + self.assertEqual(node.starting_comment, "Start of variation") def test_annotation_symbols(self): pgn = io.StringIO("1. b4?! g6 2. Bb2 Nc6? 3. Bxh8!!") @@ -2693,12 +2693,12 @@ def test_add_line(self): tail = game.add_line(moves, starting_comment="start", comment="end", nags=(17, 42)) self.assertEqual(tail.parent.move, chess.Move.from_uci("g1f3")) - self.assertEqual(tail.parent.starting_comment, ["start"]) - self.assertEqual(tail.parent.comment, []) + self.assertEqual(tail.parent.starting_comment, "start") + self.assertEqual(tail.parent.comment, "") self.assertEqual(len(tail.parent.nags), 0) self.assertEqual(tail.move, chess.Move.from_uci("d7d5")) - self.assertEqual(tail.comment, ["end"]) + self.assertEqual(tail.comment, "end") self.assertIn(42, tail.nags) def test_mainline(self): @@ -2828,7 +2828,7 @@ def test_recursion(self): def test_annotations(self): game = chess.pgn.Game() - game.comment = chess.pgn.GameNodeComment("foo [%bar] baz") + game.comment.set("foo [%bar] baz") self.assertTrue(game.clock() is None) clock = 12345 @@ -2869,7 +2869,7 @@ def test_annotations(self): game.set_clock(None) game.set_arrows([]) - self.assertEqual(game.comment, ["foo [%bar] baz"]) + self.assertEqual(game.comment, "foo [%bar] baz") def test_eval(self): game = chess.pgn.Game() @@ -2879,15 +2879,15 @@ def test_eval(self): def test_float_emt(self): game = chess.pgn.Game() - game.comment = chess.pgn.GameNodeComment("[%emt 0:00:01.234]") + game.comment.set("[%emt 0:00:01.234]") self.assertEqual(game.emt(), 1.234) game.set_emt(6.54321) - self.assertEqual(game.comment, ["[%emt 0:00:06.543]"]) + self.assertEqual(game.comment, "[%emt 0:00:06.543]") self.assertEqual(game.emt(), 6.543) game.set_emt(-70) - self.assertEqual(game.comment, ["[%emt 0:00:00]"]) # Clamped + self.assertEqual(game.comment, "[%emt 0:00:00]") # Clamped self.assertEqual(game.emt(), 0) def test_float_clk(self): @@ -2896,11 +2896,11 @@ def test_float_clk(self): self.assertEqual(game.clock(), 1.234) game.set_clock(6.54321) - self.assertEqual(game.comment, ["[%clk 0:00:06.543]"]) + self.assertEqual(game.comment, "[%clk 0:00:06.543]") self.assertEqual(game.clock(), 6.543) game.set_clock(-70) - self.assertEqual(game.comment, ["[%clk 0:00:00]"]) # Clamped + self.assertEqual(game.comment, "[%clk 0:00:00]") # Clamped self.assertEqual(game.clock(), 0) def test_node_turn(self): From b524c3a130c8a535b060af7e73e82339c97617eb Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Thu, 22 Feb 2024 04:24:26 -0800 Subject: [PATCH 009/116] Combine append() and extend() methods --- chess/pgn.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index 058c932a8..a44aea6fb 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -209,13 +209,13 @@ def remove_empty(self) -> None: """Remove empty comments from the comment list.""" self._comments = list(filter(None, self._comments)) - def append(self, new_comment: str) -> None: - """Append a new comment to the end of the comment list.""" - self._comments.append(new_comment) - - def extend(self, new_comments: list[str]) -> None: - """Append several new comments to the end of the comment list.""" - self._comments.extend(new_comments) + def append(self, new_comment: Union[str, list[str]]) -> None: + """Append one or more new comments to the end of the comment list.""" + if new_comment: + if isinstance(new_comment, str): + self._comments.append(new_comment) + else: + self._comments.extend(new_comment) def insert(self, index: int, new_comment: str) -> None: """Insert a new comment before the specified index.""" @@ -494,14 +494,7 @@ def add_line(self, moves: Iterable[chess.Move], *, comment: Union[str, list[str] starting_comment = "" # Merge comment and NAGs. - if node.comment: - if isinstance(comment, str): - node.comment.append(comment) - else: - node.comment.extend(comment) - else: - node.comment.set(comment) - + node.comment.append(comment) node.nags.update(nags) return node From 9123ebd98765ccda2b81b21118d0a7e007937d10 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Thu, 22 Feb 2024 04:30:00 -0800 Subject: [PATCH 010/116] Add missing return type annotation --- chess/pgn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chess/pgn.py b/chess/pgn.py index a44aea6fb..6fc34133c 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -193,7 +193,7 @@ class GameNodeComment: def __init__(self, comment: Union[str, list[str]] = ""): self.set(comment) - def set(self, new_comment: Union[str, list[str]]): + def set(self, new_comment: Union[str, list[str]]) -> None: """Replace the comment with a new comment or a list of comments.""" self._comments = new_comment if isinstance(new_comment, list) else [new_comment] if new_comment else [] From 4e084cc1d652fcf4070dd8eee0a5c561534631d5 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Thu, 22 Feb 2024 20:56:04 -0800 Subject: [PATCH 011/116] Remove unneeded GameNodeComment instance --- chess/pgn.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index 6fc34133c..78c238287 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -201,9 +201,8 @@ def pgn_format(self) -> str: """Create a string representation of the comments in PGN format.""" comments = list(map(lambda s: s.replace("{", ""), self._comments)) comments = list(map(lambda s: s.replace("}", "").strip(), comments)) - output_comments = GameNodeComment(comments) - output_comments.remove_empty() - return "{ " + output_comments.join(" } { ") + " }" + comments = list(filter(None, comments)) + return "{ " + " } { ".join(comments) + " }" def remove_empty(self) -> None: """Remove empty comments from the comment list.""" From 91801c873b58d20a48d533027d4f940907a4a3ac Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Thu, 22 Feb 2024 21:37:36 -0800 Subject: [PATCH 012/116] Use @property to make changes backwards compatible --- chess/pgn.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++------ test.py | 18 +++++++++--------- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index 78c238287..5cc70747a 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -233,8 +233,13 @@ def __getitem__(self, index: int) -> str: def __setitem__(self, index: int, new_comment: str) -> None: self._comments[index] = new_comment - def __add__(self, other: GameNodeComment) -> GameNodeComment: - return GameNodeComment(self._comments + other._comments) + def __add__(self, other: Union[str, list[str], GameNodeComment]) -> GameNodeComment: + if not isinstance(other, GameNodeComment): + new_node_comment = GameNodeComment(self._comments) + new_node_comment.append(other) + return new_node_comment + else: + return GameNodeComment(self._comments + other._comments) def __eq__(self, other: object) -> bool: if isinstance(other, str): @@ -260,13 +265,37 @@ class GameNode(abc.ABC): variations: List[ChildNode] """A list of child nodes.""" - comment: GameNodeComment + _comment: GameNodeComment """ A comment that goes behind the move leading to this node. Comments that occur before any moves are assigned to the root node. """ - starting_comment: GameNodeComment + @property + def comment(self) -> GameNodeComment: + return self._comment + + @comment.setter + def comment(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: + if isinstance(new_comment, GameNodeComment): + self._comment = new_comment + else: + self._comment = GameNodeComment(new_comment) + + + _starting_comment: GameNodeComment + + @property + def starting_comment(self) -> GameNodeComment: + return self._starting_comment + + @starting_comment.setter + def starting_comment(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: + if isinstance(new_comment, GameNodeComment): + self._starting_comment = new_comment + else: + self._starting_comment = GameNodeComment(new_comment) + nags: Set[int] def __init__(self, *, comment: Union[str, list[str]] = "") -> None: @@ -733,7 +762,7 @@ class ChildNode(GameNode): move: chess.Move """The move leading to this node.""" - starting_comment: GameNodeComment + _starting_comment: GameNodeComment """ A comment for the start of a variation. Only nodes that actually start a variation (:func:`~chess.pgn.GameNode.starts_variation()` @@ -741,6 +770,17 @@ class ChildNode(GameNode): a starting comment. """ + @property + def starting_comment(self) -> GameNodeComment: + return self._starting_comment + + @starting_comment.setter + def starting_comment(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: + if isinstance(new_comment, GameNodeComment): + self._starting_comment = new_comment + else: + self._starting_comment = GameNodeComment(new_comment) + nags: Set[int] """ A set of NAGs as integers. NAGs always go behind a move, so the root @@ -1767,7 +1807,7 @@ def read_game(handle: TextIO, *, Visitor: Any = GameBuilder) -> Any: line = line[close_index + 1:] if not skip_variation_depth: - visitor.visit_comment(GameNodeComment("".join(comment_lines))) + visitor.visit_comment("".join(comment_lines)) # Continue with the current line. fresh_line = False diff --git a/test.py b/test.py index f6d30a4d1..91e54e5d5 100755 --- a/test.py +++ b/test.py @@ -2078,29 +2078,29 @@ class PgnTestCase(unittest.TestCase): def test_exporter(self): game = chess.pgn.Game() - game.comment.set("Test game:") + game.comment = "Test game:" game.headers["Result"] = "*" game.headers["VeryLongHeader"] = "This is a very long header, much wider than the 80 columns that PGNs are formatted with by default" e4 = game.add_variation(game.board().parse_san("e4")) - e4.comment.set("Scandinavian Defense:") + e4.comment = "Scandinavian Defense:" e4_d5 = e4.add_variation(e4.board().parse_san("d5")) e4_h5 = e4.add_variation(e4.board().parse_san("h5")) e4_h5.nags.add(chess.pgn.NAG_MISTAKE) - e4_h5.starting_comment.set("This") - e4_h5.comment.set("is nonsense") + e4_h5.starting_comment = "This" + e4_h5.comment = "is nonsense" e4_e5 = e4.add_variation(e4.board().parse_san("e5")) e4_e5_Qf3 = e4_e5.add_variation(e4_e5.board().parse_san("Qf3")) e4_e5_Qf3.nags.add(chess.pgn.NAG_MISTAKE) e4_c5 = e4.add_variation(e4.board().parse_san("c5")) - e4_c5.comment.set("Sicilian") + e4_c5.comment = "Sicilian" e4_d5_exd5 = e4_d5.add_main_variation(e4_d5.board().parse_san("exd5")) - e4_d5_exd5.comment.set(["Best", "and the end of this example"]) + e4_d5_exd5.comment = ["Best", "and the end of this example"] # Test string exporter with various options. exporter = chess.pgn.StringExporter(headers=False, comments=False, variations=False) @@ -2828,7 +2828,7 @@ def test_recursion(self): def test_annotations(self): game = chess.pgn.Game() - game.comment.set("foo [%bar] baz") + game.comment = "foo [%bar] baz" self.assertTrue(game.clock() is None) clock = 12345 @@ -2879,7 +2879,7 @@ def test_eval(self): def test_float_emt(self): game = chess.pgn.Game() - game.comment.set("[%emt 0:00:01.234]") + game.comment = "[%emt 0:00:01.234]" self.assertEqual(game.emt(), 1.234) game.set_emt(6.54321) @@ -2892,7 +2892,7 @@ def test_float_emt(self): def test_float_clk(self): game = chess.pgn.Game() - game.comment.set("[%clk 0:00:01.234]") + game.comment = "[%clk 0:00:01.234]" self.assertEqual(game.clock(), 1.234) game.set_clock(6.54321) From 54e044ac6fc2e9288453a72b3a8db435d3380f2b Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Thu, 22 Feb 2024 21:50:47 -0800 Subject: [PATCH 013/116] Positive branch first --- chess/pgn.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index 5cc70747a..a9910f45b 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -234,12 +234,12 @@ def __setitem__(self, index: int, new_comment: str) -> None: self._comments[index] = new_comment def __add__(self, other: Union[str, list[str], GameNodeComment]) -> GameNodeComment: - if not isinstance(other, GameNodeComment): - new_node_comment = GameNodeComment(self._comments) + if isinstance(other, GameNodeComment): + return GameNodeComment(self._comments + other._comments) + else: + new_node_comment = GameNodeComment(self._comments.copy()) new_node_comment.append(other) return new_node_comment - else: - return GameNodeComment(self._comments + other._comments) def __eq__(self, other: object) -> bool: if isinstance(other, str): From aa47f1e7de3a36ff117b6b2ac9c3ef0b23bef3b8 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Thu, 22 Feb 2024 22:11:05 -0800 Subject: [PATCH 014/116] Add display methods to GameNodeComment --- chess/pgn.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/chess/pgn.py b/chess/pgn.py index a9910f45b..7695a1c68 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -251,6 +251,12 @@ def __eq__(self, other: object) -> bool: else: return False + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self._comments})" + + def __str__(self) -> str: + return self.pgn_format() + class GameNode(abc.ABC): parent: Optional[GameNode] From f1a56125104ca3c80a9c615c949d18401b465948 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Thu, 22 Feb 2024 22:57:05 -0800 Subject: [PATCH 015/116] Create method for deleting individual comments --- chess/pgn.py | 4 ++++ test.py | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/chess/pgn.py b/chess/pgn.py index 7695a1c68..fd3096013 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -220,6 +220,10 @@ def insert(self, index: int, new_comment: str) -> None: """Insert a new comment before the specified index.""" self._comments.insert(index, new_comment) + def pop(self, index: int = -1) -> None: + """Delete comment at the given index (default is the last comment).""" + self._comments.pop(index) + def join(self, joiner: str) -> str: """Join all of the comments together with a joiner string between each.""" return joiner.join(self._comments) diff --git a/test.py b/test.py index 91e54e5d5..cbc41d6dd 100755 --- a/test.py +++ b/test.py @@ -2222,6 +2222,14 @@ def test_read_game_with_multicomment_move(self): self.assertEqual(first_move.comment, "A common opening") second_move = first_move.variation(0) self.assertEqual(second_move.comment, ["A common response", "An uncommon comment"]) + second_move.comment.pop() + self.assertEqual(second_move.comment, ["A common response"]) + self.assertEqual(second_move.comment, "A common response") + second_move.comment.append("A replaced comment") + self.assertEqual(second_move.comment, ["A common response", "A replaced comment"]) + second_move.comment.pop(0) + self.assertEqual(second_move.comment, ["A replaced comment"]) + self.assertEqual(second_move.comment, "A replaced comment") def test_comment_at_eol(self): pgn = io.StringIO(textwrap.dedent("""\ From 8756e7878b2076fe9a7fe574086e43be58d4e7a2 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Wed, 28 Feb 2024 01:05:43 -0800 Subject: [PATCH 016/116] Test that GameNodeComment is iterable --- test.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test.py b/test.py index cbc41d6dd..ee770de0f 100755 --- a/test.py +++ b/test.py @@ -2226,7 +2226,11 @@ def test_read_game_with_multicomment_move(self): self.assertEqual(second_move.comment, ["A common response"]) self.assertEqual(second_move.comment, "A common response") second_move.comment.append("A replaced comment") - self.assertEqual(second_move.comment, ["A common response", "A replaced comment"]) + multiple_comments = ["A common response", "A replaced comment"] + self.assertEqual(second_move.comment, multiple_comments) + for move_comment, test_comment in zip(second_move.comment, multiple_comments): + self.assertEqual(move_comment, test_comment) + self.assertEqual(list(second_move.comment), multiple_comments) second_move.comment.pop(0) self.assertEqual(second_move.comment, ["A replaced comment"]) self.assertEqual(second_move.comment, "A replaced comment") From 603c9755e23093ddf985a603fa19f6b24878cad0 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Wed, 28 Feb 2024 01:20:01 -0800 Subject: [PATCH 017/116] Make pgn_format() code easier to read --- chess/pgn.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index fd3096013..2d7f31096 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -199,10 +199,9 @@ def set(self, new_comment: Union[str, list[str]]) -> None: def pgn_format(self) -> str: """Create a string representation of the comments in PGN format.""" - comments = list(map(lambda s: s.replace("{", ""), self._comments)) - comments = list(map(lambda s: s.replace("}", "").strip(), comments)) - comments = list(filter(None, comments)) - return "{ " + " } { ".join(comments) + " }" + comments = map(lambda s: s.replace("{", ""), self._comments) + comments = map(lambda s: s.replace("}", "").strip(), comments) + return " ".join(f"{{ {comment} }}" for comment in comments if comment) def remove_empty(self) -> None: """Remove empty comments from the comment list.""" From cce50dcacd0774d6a6595de1ce65b5f0dd7b5101 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Wed, 28 Feb 2024 01:54:19 -0800 Subject: [PATCH 018/116] Use built-in join() --- chess/pgn.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index 2d7f31096..0e0345a92 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -223,10 +223,6 @@ def pop(self, index: int = -1) -> None: """Delete comment at the given index (default is the last comment).""" self._comments.pop(index) - def join(self, joiner: str) -> str: - """Join all of the comments together with a joiner string between each.""" - return joiner.join(self._comments) - def __len__(self) -> int: return len(self._comments) @@ -236,6 +232,9 @@ def __getitem__(self, index: int) -> str: def __setitem__(self, index: int, new_comment: str) -> None: self._comments[index] = new_comment + def __iter__(self) -> Iterator[str]: + return iter(self._comments) + def __add__(self, other: Union[str, list[str], GameNodeComment]) -> GameNodeComment: if isinstance(other, GameNodeComment): return GameNodeComment(self._comments + other._comments) @@ -543,7 +542,7 @@ def eval(self) -> Optional[chess.engine.PovScore]: Complexity is `O(n)`. """ - match = EVAL_REGEX.search(self.comment.join(" ")) + match = EVAL_REGEX.search(" ".join(self.comment)) if not match: return None @@ -569,7 +568,7 @@ def eval_depth(self) -> Optional[int]: Complexity is `O(1)`. """ - match = EVAL_REGEX.search(self.comment.join(" ")) + match = EVAL_REGEX.search(" ".join(self.comment)) return int(match.group("depth")) if match and match.group("depth") else None def set_eval(self, score: Optional[chess.engine.PovScore], depth: Optional[int] = None) -> None: @@ -605,7 +604,7 @@ def arrows(self) -> List[chess.svg.Arrow]: Returns a list of :class:`arrows `. """ arrows = [] - for match in ARROWS_REGEX.finditer(self.comment.join(" ")): + for match in ARROWS_REGEX.finditer(" ".join(self.comment)): for group in match.group("arrows").split(","): arrows.append(chess.svg.Arrow.from_pgn(group)) @@ -649,7 +648,7 @@ def clock(self) -> Optional[float]: Returns the player's remaining time to the next time control after this move, in seconds. """ - match = CLOCK_REGEX.search(self.comment.join(" ")) + match = CLOCK_REGEX.search(" ".join(self.comment)) if match is None: return None return int(match.group("hours")) * 3600 + int(match.group("minutes")) * 60 + float(match.group("seconds")) @@ -687,7 +686,7 @@ def emt(self) -> Optional[float]: Returns the player's elapsed move time use for the comment of this move, in seconds. """ - match = EMT_REGEX.search(self.comment.join(" ")) + match = EMT_REGEX.search(" ".join(self.comment)) if match is None: return None return int(match.group("hours")) * 3600 + int(match.group("minutes")) * 60 + float(match.group("seconds")) From 4e695e2bc7c50066c0f84abd4206e61c917b2d03 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Wed, 28 Feb 2024 02:04:08 -0800 Subject: [PATCH 019/116] Add docstrings for methods Also, fix spacing. --- chess/pgn.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/chess/pgn.py b/chess/pgn.py index 0e0345a92..36cac2c59 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -190,7 +190,9 @@ class GameNodeComment: A class that can hold one or more comments for a GameNode. """ + def __init__(self, comment: Union[str, list[str]] = ""): + """Create a new comment.""" self.set(comment) def set(self, new_comment: Union[str, list[str]]) -> None: @@ -224,18 +226,23 @@ def pop(self, index: int = -1) -> None: self._comments.pop(index) def __len__(self) -> int: + """Get the number of comments in this node.""" return len(self._comments) def __getitem__(self, index: int) -> str: + """Get the comment at the given index.""" return self._comments[index] def __setitem__(self, index: int, new_comment: str) -> None: + """Change the comment at the given index.""" self._comments[index] = new_comment def __iter__(self) -> Iterator[str]: + """Return an iterator over all comments.""" return iter(self._comments) def __add__(self, other: Union[str, list[str], GameNodeComment]) -> GameNodeComment: + """Create a new comment set by adding two comment sets with +.""" if isinstance(other, GameNodeComment): return GameNodeComment(self._comments + other._comments) else: @@ -244,6 +251,7 @@ def __add__(self, other: Union[str, list[str], GameNodeComment]) -> GameNodeComm return new_node_comment def __eq__(self, other: object) -> bool: + """Check for equality between two comment sets.""" if isinstance(other, str): return (len(self) == 1 and self[0] == other) or (not self and not other) elif isinstance(other, list): @@ -254,9 +262,11 @@ def __eq__(self, other: object) -> bool: return False def __repr__(self) -> str: + """Return a code-like representation of the class.""" return f"{self.__class__.__name__}({self._comments})" def __str__(self) -> str: + """Return a string representation of the comments in PGN format.""" return self.pgn_format() @@ -290,7 +300,6 @@ def comment(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: else: self._comment = GameNodeComment(new_comment) - _starting_comment: GameNodeComment @property From d1dce61a45ed3784ce867ec0218d6fb3ca47e735 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Thu, 18 Apr 2024 15:53:08 +0200 Subject: [PATCH 020/116] Bump Sphinx to 7.3.6 --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index a63c6a7ca..7516f64de 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,3 @@ -Sphinx==6.1.2 +Sphinx==7.3.6 sphinxcontrib-jquery==4.1 sphinx-rtd-theme==1.3.0 From 4c7a9025e3442fb75a14eae367fd88200e210042 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Thu, 18 Apr 2024 17:40:45 +0200 Subject: [PATCH 021/116] Use typing_extensions.TypeAlias (#564) --- chess/__init__.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/chess/__init__.py b/chess/__init__.py index a9328a04b..9eeb2c5e2 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -24,15 +24,19 @@ from typing import ClassVar, Callable, Counter, Dict, Generic, Hashable, Iterable, Iterator, List, Literal, Mapping, Optional, SupportsInt, Tuple, Type, TypeVar, Union +if typing.TYPE_CHECKING: + from typing_extensions import TypeAlias + + EnPassantSpec = Literal["legal", "fen", "xfen"] -Color = bool +Color: TypeAlias = bool COLORS = [WHITE, BLACK] = [True, False] ColorName = Literal["white", "black"] COLOR_NAMES: List[ColorName] = ["black", "white"] -PieceType = int +PieceType: TypeAlias = int PIECE_TYPES = [PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING] = range(1, 7) PIECE_SYMBOLS = [None, "p", "n", "b", "r", "q", "k"] PIECE_NAMES = [None, "pawn", "knight", "bishop", "rook", "queen", "king"] @@ -157,7 +161,7 @@ class AmbiguousMoveError(ValueError): """Raised when the attempted move is ambiguous in the current position""" -Square = int +Square: TypeAlias = int SQUARES = [ A1, B1, C1, D1, E1, F1, G1, H1, A2, B2, C2, D2, E2, F2, G2, H2, @@ -233,7 +237,7 @@ def square_mirror(square: Square) -> Square: SQUARES_180 = [square_mirror(sq) for sq in SQUARES] -Bitboard = int +Bitboard: TypeAlias = int BB_EMPTY = 0 BB_ALL = 0xffff_ffff_ffff_ffff @@ -3816,7 +3820,7 @@ def __repr__(self) -> str: return f"" -IntoSquareSet = Union[SupportsInt, Iterable[Square]] +IntoSquareSet: TypeAlias = Union[SupportsInt, Iterable[Square]] class SquareSet: """ From 6af0ff4c8947486e3e3fa8e83ca88815301aaa37 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Thu, 18 Apr 2024 17:59:10 +0200 Subject: [PATCH 022/116] Expand core constant definitions More verbose, but better IDE support --- chess/__init__.py | 234 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 175 insertions(+), 59 deletions(-) diff --git a/chess/__init__.py b/chess/__init__.py index 9eeb2c5e2..f9a9ab7f5 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -32,12 +32,20 @@ Color: TypeAlias = bool -COLORS = [WHITE, BLACK] = [True, False] +WHITE: Color = True +BLACK: Color = False +COLORS: List[Color] = [WHITE, BLACK] ColorName = Literal["white", "black"] COLOR_NAMES: List[ColorName] = ["black", "white"] PieceType: TypeAlias = int -PIECE_TYPES = [PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING] = range(1, 7) +PAWN: PieceType = 1 +KNIGHT: PieceType = 2 +BISHOP: PieceType = 3 +ROOK: PieceType = 4 +QUEEN: PieceType = 5 +KING: PieceType = 6 +PIECE_TYPES: List[PieceType] = [PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING] PIECE_SYMBOLS = [None, "p", "n", "b", "r", "q", "k"] PIECE_NAMES = [None, "pawn", "knight", "bishop", "rook", "queen", "king"] @@ -162,16 +170,71 @@ class AmbiguousMoveError(ValueError): Square: TypeAlias = int -SQUARES = [ - A1, B1, C1, D1, E1, F1, G1, H1, - A2, B2, C2, D2, E2, F2, G2, H2, - A3, B3, C3, D3, E3, F3, G3, H3, - A4, B4, C4, D4, E4, F4, G4, H4, - A5, B5, C5, D5, E5, F5, G5, H5, - A6, B6, C6, D6, E6, F6, G6, H6, - A7, B7, C7, D7, E7, F7, G7, H7, - A8, B8, C8, D8, E8, F8, G8, H8, -] = range(64) +A1: Square = 0 +B1: Square = 1 +C1: Square = 2 +D1: Square = 3 +E1: Square = 4 +F1: Square = 5 +G1: Square = 6 +H1: Square = 7 +A2: Square = 8 +B2: Square = 9 +C2: Square = 10 +D2: Square = 11 +E2: Square = 12 +F2: Square = 13 +G2: Square = 14 +H2: Square = 15 +A3: Square = 16 +B3: Square = 17 +C3: Square = 18 +D3: Square = 19 +E3: Square = 20 +F3: Square = 21 +G3: Square = 22 +H3: Square = 23 +A4: Square = 24 +B4: Square = 25 +C4: Square = 26 +D4: Square = 27 +E4: Square = 28 +F4: Square = 29 +G4: Square = 30 +H4: Square = 31 +A5: Square = 32 +B5: Square = 33 +C5: Square = 34 +D5: Square = 35 +E5: Square = 36 +F5: Square = 37 +G5: Square = 38 +H5: Square = 39 +A6: Square = 40 +B6: Square = 41 +C6: Square = 42 +D6: Square = 43 +E6: Square = 44 +F6: Square = 45 +G6: Square = 46 +H6: Square = 47 +A7: Square = 48 +B7: Square = 49 +C7: Square = 50 +D7: Square = 51 +E7: Square = 52 +F7: Square = 53 +G7: Square = 54 +H7: Square = 55 +A8: Square = 56 +B8: Square = 57 +C8: Square = 58 +D8: Square = 59 +E8: Square = 60 +F8: Square = 61 +G8: Square = 62 +H8: Square = 63 +SQUARES: List[Square] = list(range(64)) SQUARE_NAMES = [f + r for r in RANK_NAMES for f in FILE_NAMES] @@ -234,53 +297,106 @@ def square_mirror(square: Square) -> Square: """Mirrors the square vertically.""" return square ^ 0x38 -SQUARES_180 = [square_mirror(sq) for sq in SQUARES] +SQUARES_180: List[Square] = [square_mirror(sq) for sq in SQUARES] Bitboard: TypeAlias = int -BB_EMPTY = 0 -BB_ALL = 0xffff_ffff_ffff_ffff - -BB_SQUARES = [ - BB_A1, BB_B1, BB_C1, BB_D1, BB_E1, BB_F1, BB_G1, BB_H1, - BB_A2, BB_B2, BB_C2, BB_D2, BB_E2, BB_F2, BB_G2, BB_H2, - BB_A3, BB_B3, BB_C3, BB_D3, BB_E3, BB_F3, BB_G3, BB_H3, - BB_A4, BB_B4, BB_C4, BB_D4, BB_E4, BB_F4, BB_G4, BB_H4, - BB_A5, BB_B5, BB_C5, BB_D5, BB_E5, BB_F5, BB_G5, BB_H5, - BB_A6, BB_B6, BB_C6, BB_D6, BB_E6, BB_F6, BB_G6, BB_H6, - BB_A7, BB_B7, BB_C7, BB_D7, BB_E7, BB_F7, BB_G7, BB_H7, - BB_A8, BB_B8, BB_C8, BB_D8, BB_E8, BB_F8, BB_G8, BB_H8, -] = [1 << sq for sq in SQUARES] - -BB_CORNERS = BB_A1 | BB_H1 | BB_A8 | BB_H8 -BB_CENTER = BB_D4 | BB_E4 | BB_D5 | BB_E5 - -BB_LIGHT_SQUARES = 0x55aa_55aa_55aa_55aa -BB_DARK_SQUARES = 0xaa55_aa55_aa55_aa55 - -BB_FILES = [ - BB_FILE_A, - BB_FILE_B, - BB_FILE_C, - BB_FILE_D, - BB_FILE_E, - BB_FILE_F, - BB_FILE_G, - BB_FILE_H, -] = [0x0101_0101_0101_0101 << i for i in range(8)] - -BB_RANKS = [ - BB_RANK_1, - BB_RANK_2, - BB_RANK_3, - BB_RANK_4, - BB_RANK_5, - BB_RANK_6, - BB_RANK_7, - BB_RANK_8, -] = [0xff << (8 * i) for i in range(8)] - -BB_BACKRANKS = BB_RANK_1 | BB_RANK_8 +BB_EMPTY: Bitboard = 0 +BB_ALL: Bitboard = 0xffff_ffff_ffff_ffff + +BB_A1: Bitboard = 1 << A1 +BB_B1: Bitboard = 1 << B1 +BB_C1: Bitboard = 1 << C1 +BB_D1: Bitboard = 1 << D1 +BB_E1: Bitboard = 1 << E1 +BB_F1: Bitboard = 1 << F1 +BB_G1: Bitboard = 1 << G1 +BB_H1: Bitboard = 1 << H1 +BB_A2: Bitboard = 1 << A2 +BB_B2: Bitboard = 1 << B2 +BB_C2: Bitboard = 1 << C2 +BB_D2: Bitboard = 1 << D2 +BB_E2: Bitboard = 1 << E2 +BB_F2: Bitboard = 1 << F2 +BB_G2: Bitboard = 1 << G2 +BB_H2: Bitboard = 1 << H2 +BB_A3: Bitboard = 1 << A3 +BB_B3: Bitboard = 1 << B3 +BB_C3: Bitboard = 1 << C3 +BB_D3: Bitboard = 1 << D3 +BB_E3: Bitboard = 1 << E3 +BB_F3: Bitboard = 1 << F3 +BB_G3: Bitboard = 1 << G3 +BB_H3: Bitboard = 1 << H3 +BB_A4: Bitboard = 1 << A4 +BB_B4: Bitboard = 1 << B4 +BB_C4: Bitboard = 1 << C4 +BB_D4: Bitboard = 1 << D4 +BB_E4: Bitboard = 1 << E4 +BB_F4: Bitboard = 1 << F4 +BB_G4: Bitboard = 1 << G4 +BB_H4: Bitboard = 1 << H4 +BB_A5: Bitboard = 1 << A5 +BB_B5: Bitboard = 1 << B5 +BB_C5: Bitboard = 1 << C5 +BB_D5: Bitboard = 1 << D5 +BB_E5: Bitboard = 1 << E5 +BB_F5: Bitboard = 1 << F5 +BB_G5: Bitboard = 1 << G5 +BB_H5: Bitboard = 1 << H5 +BB_A6: Bitboard = 1 << A6 +BB_B6: Bitboard = 1 << B6 +BB_C6: Bitboard = 1 << C6 +BB_D6: Bitboard = 1 << D6 +BB_E6: Bitboard = 1 << E6 +BB_F6: Bitboard = 1 << F6 +BB_G6: Bitboard = 1 << G6 +BB_H6: Bitboard = 1 << H6 +BB_A7: Bitboard = 1 << A7 +BB_B7: Bitboard = 1 << B7 +BB_C7: Bitboard = 1 << C7 +BB_D7: Bitboard = 1 << D7 +BB_E7: Bitboard = 1 << E7 +BB_F7: Bitboard = 1 << F7 +BB_G7: Bitboard = 1 << G7 +BB_H7: Bitboard = 1 << H7 +BB_A8: Bitboard = 1 << A8 +BB_B8: Bitboard = 1 << B8 +BB_C8: Bitboard = 1 << C8 +BB_D8: Bitboard = 1 << D8 +BB_E8: Bitboard = 1 << E8 +BB_F8: Bitboard = 1 << F8 +BB_G8: Bitboard = 1 << G8 +BB_H8: Bitboard = 1 << H8 +BB_SQUARES: List[Bitboard] = [1 << sq for sq in SQUARES] + +BB_CORNERS: Bitboard = BB_A1 | BB_H1 | BB_A8 | BB_H8 +BB_CENTER: Bitboard = BB_D4 | BB_E4 | BB_D5 | BB_E5 + +BB_LIGHT_SQUARES: Bitboard = 0x55aa_55aa_55aa_55aa +BB_DARK_SQUARES: Bitboard = 0xaa55_aa55_aa55_aa55 + +BB_FILE_A: Bitboard = 0x0101_0101_0101_0101 << 0 +BB_FILE_B: Bitboard = 0x0101_0101_0101_0101 << 1 +BB_FILE_C: Bitboard = 0x0101_0101_0101_0101 << 2 +BB_FILE_D: Bitboard = 0x0101_0101_0101_0101 << 3 +BB_FILE_E: Bitboard = 0x0101_0101_0101_0101 << 4 +BB_FILE_F: Bitboard = 0x0101_0101_0101_0101 << 5 +BB_FILE_G: Bitboard = 0x0101_0101_0101_0101 << 6 +BB_FILE_H: Bitboard = 0x0101_0101_0101_0101 << 7 +BB_FILES: List[Bitboard] = [BB_FILE_A, BB_FILE_B, BB_FILE_C, BB_FILE_D, BB_FILE_E, BB_FILE_F, BB_FILE_G, BB_FILE_H] + +BB_RANK_1: Bitboard = 0xff << (8 * 0) +BB_RANK_2: Bitboard = 0xff << (8 * 1) +BB_RANK_3: Bitboard = 0xff << (8 * 2) +BB_RANK_4: Bitboard = 0xff << (8 * 3) +BB_RANK_5: Bitboard = 0xff << (8 * 4) +BB_RANK_6: Bitboard = 0xff << (8 * 5) +BB_RANK_7: Bitboard = 0xff << (8 * 6) +BB_RANK_8: Bitboard = 0xff << (8 * 7) +BB_RANKS: List[Bitboard] = [BB_RANK_1, BB_RANK_2, BB_RANK_3, BB_RANK_4, BB_RANK_5, BB_RANK_6, BB_RANK_7, BB_RANK_8] + +BB_BACKRANKS: Bitboard = BB_RANK_1 | BB_RANK_8 def lsb(bb: Bitboard) -> int: @@ -397,9 +513,9 @@ def _sliding_attacks(square: Square, occupied: Bitboard, deltas: Iterable[int]) def _step_attacks(square: Square, deltas: Iterable[int]) -> Bitboard: return _sliding_attacks(square, BB_ALL, deltas) -BB_KNIGHT_ATTACKS = [_step_attacks(sq, [17, 15, 10, 6, -17, -15, -10, -6]) for sq in SQUARES] -BB_KING_ATTACKS = [_step_attacks(sq, [9, 8, 7, 1, -9, -8, -7, -1]) for sq in SQUARES] -BB_PAWN_ATTACKS = [[_step_attacks(sq, deltas) for sq in SQUARES] for deltas in [[-7, -9], [7, 9]]] +BB_KNIGHT_ATTACKS: List[Bitboard] = [_step_attacks(sq, [17, 15, 10, 6, -17, -15, -10, -6]) for sq in SQUARES] +BB_KING_ATTACKS: List[Bitboard] = [_step_attacks(sq, [9, 8, 7, 1, -9, -8, -7, -1]) for sq in SQUARES] +BB_PAWN_ATTACKS: List[List[Bitboard]] = [[_step_attacks(sq, deltas) for sq in SQUARES] for deltas in [[-7, -9], [7, 9]]] def _edges(square: Square) -> Bitboard: From 90d81da2416bf8e3f4ffdbdd70e6105fea8607b4 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Sat, 20 Apr 2024 21:04:40 -0700 Subject: [PATCH 023/116] Restore previous behavior of GameNode.comment GameNode.comment now returns all comments as a single string with single spaces separating the comments. GameNode.comments (plural) returns all comments as a list of strings. --- chess/pgn.py | 90 +++++++++++++++++++++++++++++++--------------------- test.py | 43 +++++++++++++++---------- 2 files changed, 80 insertions(+), 53 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index 36cac2c59..50984a64d 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -290,8 +290,8 @@ class GameNode(abc.ABC): """ @property - def comment(self) -> GameNodeComment: - return self._comment + def comment(self) -> str: + return " ".join(self._comment) @comment.setter def comment(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: @@ -300,11 +300,19 @@ def comment(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: else: self._comment = GameNodeComment(new_comment) + @property + def comments(self) -> GameNodeComment: + return self._comment + + @comments.setter + def comments(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: + self.comment = new_comment + _starting_comment: GameNodeComment @property - def starting_comment(self) -> GameNodeComment: - return self._starting_comment + def starting_comment(self) -> str: + return " ".join(self._starting_comment) @starting_comment.setter def starting_comment(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: @@ -313,6 +321,14 @@ def starting_comment(self, new_comment: Union[str, list[str], GameNodeComment]) else: self._starting_comment = GameNodeComment(new_comment) + @property + def starting_comments(self) -> GameNodeComment: + return self._starting_comment + + @starting_comments.setter + def starting_comments(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: + self._starting_comment = new_comment + nags: Set[int] def __init__(self, *, comment: Union[str, list[str]] = "") -> None: @@ -539,7 +555,7 @@ def add_line(self, moves: Iterable[chess.Move], *, comment: Union[str, list[str] starting_comment = "" # Merge comment and NAGs. - node.comment.append(comment) + node.comments.append(comment) node.nags.update(nags) return node @@ -551,7 +567,7 @@ def eval(self) -> Optional[chess.engine.PovScore]: Complexity is `O(n)`. """ - match = EVAL_REGEX.search(" ".join(self.comment)) + match = EVAL_REGEX.search(self.comment) if not match: return None @@ -577,7 +593,7 @@ def eval_depth(self) -> Optional[int]: Complexity is `O(1)`. """ - match = EVAL_REGEX.search(" ".join(self.comment)) + match = EVAL_REGEX.search(self.comment) return int(match.group("depth")) if match and match.group("depth") else None def set_eval(self, score: Optional[chess.engine.PovScore], depth: Optional[int] = None) -> None: @@ -595,15 +611,15 @@ def set_eval(self, score: Optional[chess.engine.PovScore], depth: Optional[int] eval = f"[%eval #{score.white().mate()}{depth_suffix}]" found = 0 - for index in range(len(self.comment)): - self.comment[index], found = EVAL_REGEX.subn(_condense_affix(eval), self.comment[index], count=1) + for index in range(len(self.comments)): + self.comments[index], found = EVAL_REGEX.subn(_condense_affix(eval), self.comments[index], count=1) if found: break - self.comment.remove_empty() + self.comments.remove_empty() if not found and eval: - self.comment.append(eval) + self.comments.append(eval) def arrows(self) -> List[chess.svg.Arrow]: """ @@ -613,7 +629,7 @@ def arrows(self) -> List[chess.svg.Arrow]: Returns a list of :class:`arrows `. """ arrows = [] - for match in ARROWS_REGEX.finditer(" ".join(self.comment)): + for match in ARROWS_REGEX.finditer(self.comment): for group in match.group("arrows").split(","): arrows.append(chess.svg.Arrow.from_pgn(group)) @@ -635,10 +651,10 @@ def set_arrows(self, arrows: Iterable[Union[chess.svg.Arrow, Tuple[Square, Squar pass (csl if arrow.tail == arrow.head else cal).append(arrow.pgn()) # type: ignore - for index in range(len(self.comment)): - self.comment[index] = ARROWS_REGEX.sub(_condense_affix(""), self.comment[index]) + for index in range(len(self.comments)): + self.comments[index] = ARROWS_REGEX.sub(_condense_affix(""), self.comments[index]) - self.comment.remove_empty() + self.comments.remove_empty() prefix = "" if csl: @@ -647,7 +663,7 @@ def set_arrows(self, arrows: Iterable[Union[chess.svg.Arrow, Tuple[Square, Squar prefix += f"[%cal {','.join(cal)}]" if prefix: - self.comment.insert(0, prefix) + self.comments.insert(0, prefix) def clock(self) -> Optional[float]: """ @@ -657,7 +673,7 @@ def clock(self) -> Optional[float]: Returns the player's remaining time to the next time control after this move, in seconds. """ - match = CLOCK_REGEX.search(" ".join(self.comment)) + match = CLOCK_REGEX.search(self.comment) if match is None: return None return int(match.group("hours")) * 3600 + int(match.group("minutes")) * 60 + float(match.group("seconds")) @@ -677,15 +693,15 @@ def set_clock(self, seconds: Optional[float]) -> None: clk = f"[%clk {hours:d}:{minutes:02d}:{seconds_part}]" found = 0 - for index in range(len(self.comment)): - self.comment[index], found = CLOCK_REGEX.subn(_condense_affix(clk), self.comment[index], count=1) + for index in range(len(self.comments)): + self.comments[index], found = CLOCK_REGEX.subn(_condense_affix(clk), self.comments[index], count=1) if found: break - self.comment.remove_empty() + self.comments.remove_empty() if not found and clk: - self.comment.append(clk) + self.comments.append(clk) def emt(self) -> Optional[float]: """ @@ -695,7 +711,7 @@ def emt(self) -> Optional[float]: Returns the player's elapsed move time use for the comment of this move, in seconds. """ - match = EMT_REGEX.search(" ".join(self.comment)) + match = EMT_REGEX.search(" ".join(self.comments)) if match is None: return None return int(match.group("hours")) * 3600 + int(match.group("minutes")) * 60 + float(match.group("seconds")) @@ -715,15 +731,15 @@ def set_emt(self, seconds: Optional[float]) -> None: emt = f"[%emt {hours:d}:{minutes:02d}:{seconds_part}]" found = 0 - for index in range(len(self.comment)): - self.comment[index], found = EMT_REGEX.subn(_condense_affix(emt), self.comment[index], count=1) + for index in range(len(self.comments)): + self.comments[index], found = EMT_REGEX.subn(_condense_affix(emt), self.comments[index], count=1) if found: break - self.comment.remove_empty() + self.comments.remove_empty() if not found and emt: - self.comment.append(emt) + self.comments.append(emt) @abc.abstractmethod def accept(self, visitor: BaseVisitor[ResultT]) -> ResultT: @@ -868,7 +884,7 @@ def end(self) -> ChildNode: def _accept_node(self, parent_board: chess.Board, visitor: BaseVisitor[ResultT]) -> None: if self.starting_comment: - visitor.visit_comment(self.starting_comment) + visitor.visit_comment(self.starting_comments) visitor.visit_move(parent_board, self.move) @@ -879,8 +895,8 @@ def _accept_node(self, parent_board: chess.Board, visitor: BaseVisitor[ResultT]) for nag in sorted(self.nags): visitor.visit_nag(nag) - if self.comment: - visitor.visit_comment(self.comment) + if self.comments: + visitor.visit_comment(self.comments) def _accept(self, parent_board: chess.Board, visitor: BaseVisitor[ResultT], *, sidelines: bool = True) -> None: stack = [_AcceptFrame(self, sidelines=sidelines)] @@ -1011,8 +1027,8 @@ def accept(self, visitor: BaseVisitor[ResultT]) -> ResultT: board = self.board() visitor.visit_board(board) - if self.comment: - visitor.visit_comment(self.comment) + if self.comments: + visitor.visit_comment(self.comments) if self.variations: self.variations[0]._accept(board, visitor) @@ -1347,14 +1363,14 @@ def visit_comment(self, comment: GameNodeComment) -> None: # Add as a comment for the current node if in the middle of # a variation. Add as a comment for the game if the comment # starts before any move. - new_comment = self.variation_stack[-1].comment + comment - new_comment.remove_empty() - self.variation_stack[-1].comment = new_comment + new_comments = self.variation_stack[-1].comments + comment + new_comments.remove_empty() + self.variation_stack[-1].comments = new_comments else: # Otherwise, it is a starting comment. - new_comment = self.starting_comment + comment - new_comment.remove_empty() - self.starting_comment = new_comment + new_comments = self.starting_comment + comment + new_comments.remove_empty() + self.starting_comment = new_comments def visit_move(self, board: chess.Board, move: chess.Move) -> None: self.variation_stack[-1] = self.variation_stack[-1].add_variation(move) diff --git a/test.py b/test.py index ee770de0f..aa89cd2c5 100755 --- a/test.py +++ b/test.py @@ -2220,19 +2220,24 @@ def test_read_game_with_multicomment_move(self): game = chess.pgn.read_game(pgn) first_move = game.variation(0) self.assertEqual(first_move.comment, "A common opening") + self.assertEqual(first_move.comments, "A common opening") + self.assertEqual(first_move.comments, ["A common opening"]) second_move = first_move.variation(0) - self.assertEqual(second_move.comment, ["A common response", "An uncommon comment"]) - second_move.comment.pop() - self.assertEqual(second_move.comment, ["A common response"]) + self.assertEqual(second_move.comments, ["A common response", "An uncommon comment"]) + second_move.comments.pop() + self.assertEqual(second_move.comments, ["A common response"]) + self.assertEqual(second_move.comments, "A common response") self.assertEqual(second_move.comment, "A common response") - second_move.comment.append("A replaced comment") + second_move.comments.append("A replaced comment") multiple_comments = ["A common response", "A replaced comment"] - self.assertEqual(second_move.comment, multiple_comments) - for move_comment, test_comment in zip(second_move.comment, multiple_comments): + self.assertEqual(second_move.comments, multiple_comments) + for move_comment, test_comment in zip(second_move.comments, multiple_comments): self.assertEqual(move_comment, test_comment) - self.assertEqual(list(second_move.comment), multiple_comments) - second_move.comment.pop(0) - self.assertEqual(second_move.comment, ["A replaced comment"]) + self.assertEqual(list(second_move.comments), multiple_comments) + self.assertEqual(second_move.comment, " ".join(multiple_comments)) + second_move.comments.pop(0) + self.assertEqual(second_move.comments, ["A replaced comment"]) + self.assertEqual(second_move.comments, "A replaced comment") self.assertEqual(second_move.comment, "A replaced comment") def test_comment_at_eol(self): @@ -2845,22 +2850,24 @@ def test_annotations(self): self.assertTrue(game.clock() is None) clock = 12345 game.set_clock(clock) - self.assertEqual(game.comment, ["foo [%bar] baz", "[%clk 3:25:45]"]) + self.assertEqual(game.comment, "foo [%bar] baz [%clk 3:25:45]") + self.assertEqual(game.comments, ["foo [%bar] baz", "[%clk 3:25:45]"]) self.assertEqual(game.clock(), clock) self.assertTrue(game.eval() is None) game.set_eval(chess.engine.PovScore(chess.engine.Cp(-80), chess.WHITE)) - self.assertEqual(game.comment, ["foo [%bar] baz", "[%clk 3:25:45]", "[%eval -0.80]"]) + self.assertEqual(game.comments, ["foo [%bar] baz", "[%clk 3:25:45]", "[%eval -0.80]"]) self.assertEqual(game.eval().white().score(), -80) self.assertEqual(game.eval_depth(), None) game.set_eval(chess.engine.PovScore(chess.engine.Mate(1), chess.WHITE), 5) - self.assertEqual(game.comment, ["foo [%bar] baz", "[%clk 3:25:45]", "[%eval #1,5]"]) + self.assertEqual(game.comments, ["foo [%bar] baz", "[%clk 3:25:45]", "[%eval #1,5]"]) self.assertEqual(game.eval().white().mate(), 1) self.assertEqual(game.eval_depth(), 5) self.assertEqual(game.arrows(), []) game.set_arrows([(chess.A1, chess.A1), chess.svg.Arrow(chess.A1, chess.H1, color="red"), chess.svg.Arrow(chess.B1, chess.B8)]) - self.assertEqual(game.comment, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]", "[%eval #1,5]"]) + self.assertEqual(game.comments, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]", "[%eval #1,5]"]) + self.assertEqual(game.comment, "[%csl Ga1][%cal Ra1h1,Gb1b8] foo [%bar] baz [%clk 3:25:45] [%eval #1,5]") arrows = game.arrows() self.assertEqual(len(arrows), 3) self.assertEqual(arrows[0].color, "green") @@ -2870,17 +2877,21 @@ def test_annotations(self): self.assertTrue(game.emt() is None) emt = 321 game.set_emt(emt) - self.assertEqual(game.comment, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]", "[%eval #1,5]", "[%emt 0:05:21]"]) + self.assertEqual(game.comments, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]", "[%eval #1,5]", "[%emt 0:05:21]"]) + self.assertEqual(game.comment, "[%csl Ga1][%cal Ra1h1,Gb1b8] foo [%bar] baz [%clk 3:25:45] [%eval #1,5] [%emt 0:05:21]") self.assertEqual(game.emt(), emt) game.set_eval(None) - self.assertEqual(game.comment, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]", "[%emt 0:05:21]"]) + self.assertEqual(game.comments, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]", "[%emt 0:05:21]"]) + self.assertEqual(game.comment, "[%csl Ga1][%cal Ra1h1,Gb1b8] foo [%bar] baz [%clk 3:25:45] [%emt 0:05:21]") game.set_emt(None) - self.assertEqual(game.comment, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]"]) + self.assertEqual(game.comments, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]"]) + self.assertEqual(game.comment, "[%csl Ga1][%cal Ra1h1,Gb1b8] foo [%bar] baz [%clk 3:25:45]") game.set_clock(None) game.set_arrows([]) + self.assertEqual(game.comments, "foo [%bar] baz") self.assertEqual(game.comment, "foo [%bar] baz") def test_eval(self): From 2761d7c2b129bb5c88b3ff59c2dbbc792863b172 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Sat, 20 Apr 2024 21:16:52 -0700 Subject: [PATCH 024/116] Fix typing errors --- chess/pgn.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index 50984a64d..c70c47bc2 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -306,7 +306,10 @@ def comments(self) -> GameNodeComment: @comments.setter def comments(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: - self.comment = new_comment + if isinstance(new_comment, GameNodeComment): + self._comment = new_comment + else: + self._comment = GameNodeComment(new_comment) _starting_comment: GameNodeComment @@ -327,7 +330,10 @@ def starting_comments(self) -> GameNodeComment: @starting_comments.setter def starting_comments(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: - self._starting_comment = new_comment + if isinstance(new_comment, GameNodeComment): + self._starting_comment = new_comment + else: + self._starting_comment = GameNodeComment(new_comment) nags: Set[int] @@ -335,11 +341,11 @@ def __init__(self, *, comment: Union[str, list[str]] = "") -> None: self.parent = None self.move = None self.variations = [] - self.comment = GameNodeComment(comment) + self.comments = GameNodeComment(comment) # Deprecated: These should be properties of ChildNode, but need to # remain here for backwards compatibility. - self.starting_comment = GameNodeComment() + self.starting_comments = GameNodeComment() self.nags = set() @abc.abstractmethod @@ -804,8 +810,8 @@ class ChildNode(GameNode): """ @property - def starting_comment(self) -> GameNodeComment: - return self._starting_comment + def starting_comment(self) -> str: + return " ".join(self._starting_comment) @starting_comment.setter def starting_comment(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: @@ -827,7 +833,7 @@ def __init__(self, parent: GameNode, move: chess.Move, *, comment: Union[str, li self.parent.variations.append(self) self.nags.update(nags) - self.starting_comment = GameNodeComment(starting_comment) + self.starting_comments = GameNodeComment(starting_comment) def board(self) -> chess.Board: stack: List[chess.Move] = [] @@ -1374,7 +1380,7 @@ def visit_comment(self, comment: GameNodeComment) -> None: def visit_move(self, board: chess.Board, move: chess.Move) -> None: self.variation_stack[-1] = self.variation_stack[-1].add_variation(move) - self.variation_stack[-1].starting_comment = self.starting_comment + self.variation_stack[-1].starting_comments = self.starting_comment self.starting_comment = GameNodeComment() self.in_variation = True From f0baba73ba034d48cb734bf4c0848417396a633f Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Sat, 20 Apr 2024 21:22:56 -0700 Subject: [PATCH 025/116] Consolidate repeated logic --- chess/pgn.py | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index c70c47bc2..6ff13fd7e 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -191,9 +191,9 @@ class GameNodeComment: A class that can hold one or more comments for a GameNode. """ - def __init__(self, comment: Union[str, list[str]] = ""): + def __init__(self, comment: Union[str, list[str], GameNodeComment] = ""): """Create a new comment.""" - self.set(comment) + self.set(comment._comments if isinstance(comment,GameNodeComment) else comment) def set(self, new_comment: Union[str, list[str]]) -> None: """Replace the comment with a new comment or a list of comments.""" @@ -295,10 +295,7 @@ def comment(self) -> str: @comment.setter def comment(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: - if isinstance(new_comment, GameNodeComment): - self._comment = new_comment - else: - self._comment = GameNodeComment(new_comment) + self._comment = GameNodeComment(new_comment) @property def comments(self) -> GameNodeComment: @@ -306,10 +303,7 @@ def comments(self) -> GameNodeComment: @comments.setter def comments(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: - if isinstance(new_comment, GameNodeComment): - self._comment = new_comment - else: - self._comment = GameNodeComment(new_comment) + self._comment = GameNodeComment(new_comment) _starting_comment: GameNodeComment @@ -319,10 +313,7 @@ def starting_comment(self) -> str: @starting_comment.setter def starting_comment(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: - if isinstance(new_comment, GameNodeComment): - self._starting_comment = new_comment - else: - self._starting_comment = GameNodeComment(new_comment) + self._starting_comment = GameNodeComment(new_comment) @property def starting_comments(self) -> GameNodeComment: @@ -330,10 +321,7 @@ def starting_comments(self) -> GameNodeComment: @starting_comments.setter def starting_comments(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: - if isinstance(new_comment, GameNodeComment): - self._starting_comment = new_comment - else: - self._starting_comment = GameNodeComment(new_comment) + self._starting_comment = GameNodeComment(new_comment) nags: Set[int] @@ -815,10 +803,7 @@ def starting_comment(self) -> str: @starting_comment.setter def starting_comment(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: - if isinstance(new_comment, GameNodeComment): - self._starting_comment = new_comment - else: - self._starting_comment = GameNodeComment(new_comment) + self._starting_comment = GameNodeComment(new_comment) nags: Set[int] """ From f840f8e58e635d89ba810ffa08cb0f81d4925f79 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Sun, 21 Apr 2024 19:27:56 -0700 Subject: [PATCH 026/116] Revert unneeded code change --- chess/pgn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chess/pgn.py b/chess/pgn.py index 6ff13fd7e..e462a0750 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -705,7 +705,7 @@ def emt(self) -> Optional[float]: Returns the player's elapsed move time use for the comment of this move, in seconds. """ - match = EMT_REGEX.search(" ".join(self.comments)) + match = EMT_REGEX.search(self.comment) if match is None: return None return int(match.group("hours")) * 3600 + int(match.group("minutes")) * 60 + float(match.group("seconds")) From 716a0b9459480b852b5169a0f1b0b3dbecd3be31 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 4 May 2024 17:44:20 +0200 Subject: [PATCH 027/116] Require that EPD opcodes start with letter (fixes #1080) --- chess/__init__.py | 16 +++++++++++++--- test.py | 5 +++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/chess/__init__.py b/chess/__init__.py index f9a9ab7f5..bed9eec5d 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -2739,9 +2739,7 @@ def _epd_operations(self, operations: Mapping[str, Union[None, str, int, float, first_op = True for opcode, operand in operations.items(): - assert opcode != "-", "dash (-) is not a valid epd opcode" - for blacklisted in [" ", "\n", "\t", "\r"]: - assert blacklisted not in opcode, f"invalid character {blacklisted!r} in epd opcode: {opcode!r}" + self._validate_epd_opcode(opcode) if not first_op: epd.append(" ") @@ -2819,6 +2817,17 @@ def epd(self, *, shredder: bool = False, en_passant: EnPassantSpec = "legal", pr return " ".join(epd) + def _validate_epd_opcode(self, opcode: str) -> None: + if not opcode: + raise ValueError("empty string is not a valid epd opcode") + if opcode == "-": + raise ValueError("dash (-) is not a valid epd opcode") + if not opcode[0].isalpha(): + raise ValueError(f"expected epd opcode to start with a letter, got: {opcode!r}") + for blacklisted in [" ", "\n", "\t", "\r"]: + if blacklisted in opcode: + raise ValueError(f"invalid character {blacklisted!r} in epd opcode: {opcode!r}") + def _parse_epd_ops(self: BoardT, operation_part: str, make_board: Callable[[], BoardT]) -> Dict[str, Union[None, str, int, float, Move, List[Move]]]: operations: Dict[str, Union[None, str, int, float, Move, List[Move]]] = {} state = "opcode" @@ -2832,6 +2841,7 @@ def _parse_epd_ops(self: BoardT, operation_part: str, make_board: Callable[[], B if opcode == "-": opcode = "" elif opcode: + self._validate_epd_opcode(opcode) state = "after_opcode" elif ch is None or ch == ";": if opcode == "-": diff --git a/test.py b/test.py index 8dbc95a25..4ca3be6c2 100755 --- a/test.py +++ b/test.py @@ -1143,6 +1143,11 @@ def test_eret_epd(self): self.assertEqual(ops["id"], "ERET 001 - Entlastung") self.assertEqual(ops["bm"], [chess.Move.from_uci("f1f4")]) + def test_set_fen_as_epd(self): + board = chess.Board() + with self.assertRaises(ValueError): + board.set_epd(board.fen()) # Move numbers are not valid opcodes + def test_null_moves(self): self.assertEqual(str(chess.Move.null()), "0000") self.assertEqual(chess.Move.null().uci(), "0000") From df714e39a559135c51228cca6a7cb5c716756ee3 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 4 May 2024 17:59:31 +0200 Subject: [PATCH 028/116] Fixup invalid EPDs in chess960.perft file --- examples/perft/chess960.perft | 1920 ++++++++++++++++----------------- 1 file changed, 960 insertions(+), 960 deletions(-) diff --git a/examples/perft/chess960.perft b/examples/perft/chess960.perft index 76be26eed..12276f256 100644 --- a/examples/perft/chess960.perft +++ b/examples/perft/chess960.perft @@ -2,7 +2,7 @@ # https://github.com/AndyGrant/Ethereal/blob/master/src/perft/fischer.epd id 0 -epd bqnb1rkr/pp3ppp/3ppn2/2p5/5P2/P2P4/NPP1P1PP/BQ1BNRKR w HFhf - 2 9 +epd bqnb1rkr/pp3ppp/3ppn2/2p5/5P2/P2P4/NPP1P1PP/BQ1BNRKR w HFhf - perft 1 21 perft 2 528 perft 3 12189 @@ -11,7 +11,7 @@ perft 5 8146062 perft 6 227689589 id 1 -epd 2nnrbkr/p1qppppp/8/1ppb4/6PP/3PP3/PPP2P2/BQNNRBKR w HEhe - 1 9 +epd 2nnrbkr/p1qppppp/8/1ppb4/6PP/3PP3/PPP2P2/BQNNRBKR w HEhe - perft 1 21 perft 2 807 perft 3 18002 @@ -20,7 +20,7 @@ perft 5 16253601 perft 6 590751109 id 2 -epd b1q1rrkb/pppppppp/3nn3/8/P7/1PPP4/4PPPP/BQNNRKRB w GE - 1 9 +epd b1q1rrkb/pppppppp/3nn3/8/P7/1PPP4/4PPPP/BQNNRKRB w GE - perft 1 20 perft 2 479 perft 3 10471 @@ -29,7 +29,7 @@ perft 5 6417013 perft 6 177654692 id 3 -epd qbbnnrkr/2pp2pp/p7/1p2pp2/8/P3PP2/1PPP1KPP/QBBNNR1R w hf - 0 9 +epd qbbnnrkr/2pp2pp/p7/1p2pp2/8/P3PP2/1PPP1KPP/QBBNNR1R w hf - perft 1 22 perft 2 593 perft 3 13440 @@ -38,7 +38,7 @@ perft 5 9183776 perft 6 274103539 id 4 -epd 1nbbnrkr/p1p1ppp1/3p4/1p3P1p/3Pq2P/8/PPP1P1P1/QNBBNRKR w HFhf - 0 9 +epd 1nbbnrkr/p1p1ppp1/3p4/1p3P1p/3Pq2P/8/PPP1P1P1/QNBBNRKR w HFhf - perft 1 28 perft 2 1120 perft 3 31058 @@ -47,7 +47,7 @@ perft 5 34030312 perft 6 1250970898 id 5 -epd qnbnr1kr/ppp1b1pp/4p3/3p1p2/8/2NPP3/PPP1BPPP/QNB1R1KR w HEhe - 1 9 +epd qnbnr1kr/ppp1b1pp/4p3/3p1p2/8/2NPP3/PPP1BPPP/QNB1R1KR w HEhe - perft 1 29 perft 2 899 perft 3 26578 @@ -56,7 +56,7 @@ perft 5 24851983 perft 6 775718317 id 6 -epd q1bnrkr1/ppppp2p/2n2p2/4b1p1/2NP4/8/PPP1PPPP/QNB1RRKB w ge - 1 9 +epd q1bnrkr1/ppppp2p/2n2p2/4b1p1/2NP4/8/PPP1PPPP/QNB1RRKB w ge - perft 1 30 perft 2 860 perft 3 24566 @@ -65,7 +65,7 @@ perft 5 21093346 perft 6 649209803 id 7 -epd qbn1brkr/ppp1p1p1/2n4p/3p1p2/P7/6PP/QPPPPP2/1BNNBRKR w HFhf - 0 9 +epd qbn1brkr/ppp1p1p1/2n4p/3p1p2/P7/6PP/QPPPPP2/1BNNBRKR w HFhf - perft 1 25 perft 2 635 perft 3 17054 @@ -74,7 +74,7 @@ perft 5 13203304 perft 6 377184252 id 8 -epd qnnbbrkr/1p2ppp1/2pp3p/p7/1P5P/2NP4/P1P1PPP1/Q1NBBRKR w HFhf - 0 9 +epd qnnbbrkr/1p2ppp1/2pp3p/p7/1P5P/2NP4/P1P1PPP1/Q1NBBRKR w HFhf - perft 1 24 perft 2 572 perft 3 15243 @@ -83,7 +83,7 @@ perft 5 11110203 perft 6 293989890 id 9 -epd qn1rbbkr/ppp2p1p/1n1pp1p1/8/3P4/P6P/1PP1PPPK/QNNRBB1R w hd - 2 9 +epd qn1rbbkr/ppp2p1p/1n1pp1p1/8/3P4/P6P/1PP1PPPK/QNNRBB1R w hd - perft 1 28 perft 2 811 perft 3 23175 @@ -92,7 +92,7 @@ perft 5 19836606 perft 6 594527992 id 10 -epd qnr1bkrb/pppp2pp/3np3/5p2/8/P2P2P1/NPP1PP1P/QN1RBKRB w GDg - 3 9 +epd qnr1bkrb/pppp2pp/3np3/5p2/8/P2P2P1/NPP1PP1P/QN1RBKRB w GDg - perft 1 33 perft 2 823 perft 3 26895 @@ -101,7 +101,7 @@ perft 5 23114629 perft 6 646390782 id 11 -epd qb1nrkbr/1pppp1p1/1n3p2/p1B4p/8/3P1P1P/PPP1P1P1/QBNNRK1R w HEhe - 0 9 +epd qb1nrkbr/1pppp1p1/1n3p2/p1B4p/8/3P1P1P/PPP1P1P1/QBNNRK1R w HEhe - perft 1 31 perft 2 855 perft 3 25620 @@ -110,7 +110,7 @@ perft 5 21796206 perft 6 651054626 id 12 -epd qnnbrk1r/1p1ppbpp/2p5/p4p2/2NP3P/8/PPP1PPP1/Q1NBRKBR w HEhe - 0 9 +epd qnnbrk1r/1p1ppbpp/2p5/p4p2/2NP3P/8/PPP1PPP1/Q1NBRKBR w HEhe - perft 1 26 perft 2 790 perft 3 21238 @@ -119,7 +119,7 @@ perft 5 17819770 perft 6 544866674 id 13 -epd 1qnrkbbr/1pppppp1/p1n4p/8/P7/1P1N1P2/2PPP1PP/QN1RKBBR w HDhd - 0 9 +epd 1qnrkbbr/1pppppp1/p1n4p/8/P7/1P1N1P2/2PPP1PP/QN1RKBBR w HDhd - perft 1 37 perft 2 883 perft 3 32187 @@ -128,7 +128,7 @@ perft 5 29370838 perft 6 783201510 id 14 -epd qn1rkrbb/pp1p1ppp/2p1p3/3n4/4P2P/2NP4/PPP2PP1/Q1NRKRBB w FDfd - 1 9 +epd qn1rkrbb/pp1p1ppp/2p1p3/3n4/4P2P/2NP4/PPP2PP1/Q1NRKRBB w FDfd - perft 1 24 perft 2 585 perft 3 14769 @@ -137,7 +137,7 @@ perft 5 9482310 perft 6 233468620 id 15 -epd bb1qnrkr/pp1p1pp1/1np1p3/4N2p/8/1P4P1/P1PPPP1P/BBNQ1RKR w HFhf - 0 9 +epd bb1qnrkr/pp1p1pp1/1np1p3/4N2p/8/1P4P1/P1PPPP1P/BBNQ1RKR w HFhf - perft 1 29 perft 2 864 perft 3 25747 @@ -146,7 +146,7 @@ perft 5 24219627 perft 6 776836316 id 16 -epd bnqbnr1r/p1p1ppkp/3p4/1p4p1/P7/3NP2P/1PPP1PP1/BNQB1RKR w HF - 0 9 +epd bnqbnr1r/p1p1ppkp/3p4/1p4p1/P7/3NP2P/1PPP1PP1/BNQB1RKR w HF - perft 1 26 perft 2 889 perft 3 24353 @@ -155,7 +155,7 @@ perft 5 23701014 perft 6 809194268 id 17 -epd bnqnrbkr/1pp2pp1/p7/3pP2p/4P1P1/8/PPPP3P/BNQNRBKR w HEhe d6 0 9 +epd bnqnrbkr/1pp2pp1/p7/3pP2p/4P1P1/8/PPPP3P/BNQNRBKR w HEhe d6 perft 1 31 perft 2 984 perft 3 28677 @@ -164,7 +164,7 @@ perft 5 29032175 perft 6 1008880643 id 18 -epd b1qnrrkb/ppp1pp1p/n2p1Pp1/8/8/P7/1PPPP1PP/BNQNRKRB w GE - 0 9 +epd b1qnrrkb/ppp1pp1p/n2p1Pp1/8/8/P7/1PPPP1PP/BNQNRKRB w GE - perft 1 20 perft 2 484 perft 3 10532 @@ -173,7 +173,7 @@ perft 5 6718715 perft 6 193594729 id 19 -epd n1bqnrkr/pp1ppp1p/2p5/6p1/2P2b2/PN6/1PNPPPPP/1BBQ1RKR w HFhf - 2 9 +epd n1bqnrkr/pp1ppp1p/2p5/6p1/2P2b2/PN6/1PNPPPPP/1BBQ1RKR w HFhf - perft 1 23 perft 2 732 perft 3 17746 @@ -182,7 +182,7 @@ perft 5 14481581 perft 6 457140569 id 20 -epd n1bb1rkr/qpnppppp/2p5/p7/P1P5/5P2/1P1PPRPP/NQBBN1KR w Hhf - 1 9 +epd n1bb1rkr/qpnppppp/2p5/p7/P1P5/5P2/1P1PPRPP/NQBBN1KR w Hhf - perft 1 27 perft 2 697 perft 3 18724 @@ -191,7 +191,7 @@ perft 5 14226907 perft 6 400942568 id 21 -epd nqb1rbkr/pppppp1p/4n3/6p1/4P3/1NP4P/PP1P1PP1/1QBNRBKR w HEhe - 1 9 +epd nqb1rbkr/pppppp1p/4n3/6p1/4P3/1NP4P/PP1P1PP1/1QBNRBKR w HEhe - perft 1 28 perft 2 641 perft 3 18811 @@ -200,7 +200,7 @@ perft 5 13780398 perft 6 354122358 id 22 -epd n1bnrrkb/pp1pp2p/2p2p2/6p1/5B2/3P4/PPP1PPPP/NQ1NRKRB w GE - 2 9 +epd n1bnrrkb/pp1pp2p/2p2p2/6p1/5B2/3P4/PPP1PPPP/NQ1NRKRB w GE - perft 1 28 perft 2 606 perft 3 16883 @@ -209,7 +209,7 @@ perft 5 10815324 perft 6 254026570 id 23 -epd nbqnbrkr/2ppp1p1/pp3p1p/8/4N2P/1N6/PPPPPPP1/1BQ1BRKR w HFhf - 0 9 +epd nbqnbrkr/2ppp1p1/pp3p1p/8/4N2P/1N6/PPPPPPP1/1BQ1BRKR w HFhf - perft 1 26 perft 2 626 perft 3 17268 @@ -218,7 +218,7 @@ perft 5 12719546 perft 6 339132046 id 24 -epd nq1bbrkr/pp2nppp/2pp4/4p3/1PP1P3/1B6/P2P1PPP/NQN1BRKR w HFhf - 2 9 +epd nq1bbrkr/pp2nppp/2pp4/4p3/1PP1P3/1B6/P2P1PPP/NQN1BRKR w HFhf - perft 1 21 perft 2 504 perft 3 11812 @@ -227,7 +227,7 @@ perft 5 7697880 perft 6 207028745 id 25 -epd nqnrb1kr/2pp1ppp/1p1bp3/p1B5/5P2/3N4/PPPPP1PP/NQ1R1BKR w HDhd - 0 9 +epd nqnrb1kr/2pp1ppp/1p1bp3/p1B5/5P2/3N4/PPPPP1PP/NQ1R1BKR w HDhd - perft 1 30 perft 2 672 perft 3 19307 @@ -236,7 +236,7 @@ perft 5 13454573 perft 6 345445468 id 26 -epd nqn2krb/p1prpppp/1pbp4/7P/5P2/8/PPPPPKP1/NQNRB1RB w g - 3 9 +epd nqn2krb/p1prpppp/1pbp4/7P/5P2/8/PPPPPKP1/NQNRB1RB w g - perft 1 21 perft 2 461 perft 3 10608 @@ -245,7 +245,7 @@ perft 5 6194124 perft 6 152861936 id 27 -epd nb1n1kbr/ppp1rppp/3pq3/P3p3/8/4P3/1PPPRPPP/NBQN1KBR w Hh - 1 9 +epd nb1n1kbr/ppp1rppp/3pq3/P3p3/8/4P3/1PPPRPPP/NBQN1KBR w Hh - perft 1 19 perft 2 566 perft 3 11786 @@ -254,7 +254,7 @@ perft 5 8047916 perft 6 249171636 id 28 -epd nqnbrkbr/1ppppp1p/p7/6p1/6P1/P6P/1PPPPP2/NQNBRKBR w HEhe - 1 9 +epd nqnbrkbr/1ppppp1p/p7/6p1/6P1/P6P/1PPPPP2/NQNBRKBR w HEhe - perft 1 20 perft 2 382 perft 3 8694 @@ -263,7 +263,7 @@ perft 5 4708975 perft 6 112278808 id 29 -epd nq1rkb1r/pp1pp1pp/1n2bp1B/2p5/8/5P1P/PPPPP1P1/NQNRKB1R w HDhd - 2 9 +epd nq1rkb1r/pp1pp1pp/1n2bp1B/2p5/8/5P1P/PPPPP1P1/NQNRKB1R w HDhd - perft 1 24 perft 2 809 perft 3 20090 @@ -272,7 +272,7 @@ perft 5 17647882 perft 6 593457788 id 30 -epd nqnrkrb1/pppppp2/7p/4b1p1/8/PN1NP3/1PPP1PPP/1Q1RKRBB w FDfd - 1 9 +epd nqnrkrb1/pppppp2/7p/4b1p1/8/PN1NP3/1PPP1PPP/1Q1RKRBB w FDfd - perft 1 26 perft 2 683 perft 3 18102 @@ -281,7 +281,7 @@ perft 5 13055173 perft 6 352398011 id 31 -epd bb1nqrkr/1pp1ppp1/pn5p/3p4/8/P2NNP2/1PPPP1PP/BB2QRKR w HFhf - 0 9 +epd bb1nqrkr/1pp1ppp1/pn5p/3p4/8/P2NNP2/1PPPP1PP/BB2QRKR w HFhf - perft 1 29 perft 2 695 perft 3 21193 @@ -290,7 +290,7 @@ perft 5 17454857 perft 6 483785639 id 32 -epd bnn1qrkr/pp1ppp1p/2p5/b3Q1p1/8/5P1P/PPPPP1P1/BNNB1RKR w HFhf - 2 9 +epd bnn1qrkr/pp1ppp1p/2p5/b3Q1p1/8/5P1P/PPPPP1P1/BNNB1RKR w HFhf - perft 1 44 perft 2 920 perft 3 35830 @@ -299,7 +299,7 @@ perft 5 29742670 perft 6 702867204 id 33 -epd bnnqrbkr/pp1p2p1/2p1p2p/5p2/1P5P/1R6/P1PPPPP1/BNNQRBK1 w Ehe - 0 9 +epd bnnqrbkr/pp1p2p1/2p1p2p/5p2/1P5P/1R6/P1PPPPP1/BNNQRBK1 w Ehe - perft 1 33 perft 2 1022 perft 3 32724 @@ -308,7 +308,7 @@ perft 5 32898113 perft 6 1047360456 id 34 -epd b1nqrkrb/2pppppp/p7/1P6/1n6/P4P2/1P1PP1PP/BNNQRKRB w GEge - 0 9 +epd b1nqrkrb/2pppppp/p7/1P6/1n6/P4P2/1P1PP1PP/BNNQRKRB w GEge - perft 1 23 perft 2 638 perft 3 15744 @@ -317,7 +317,7 @@ perft 5 11735969 perft 6 344211589 id 35 -epd n1bnqrkr/3ppppp/1p6/pNp1b3/2P3P1/8/PP1PPP1P/NBB1QRKR w HFhf - 1 9 +epd n1bnqrkr/3ppppp/1p6/pNp1b3/2P3P1/8/PP1PPP1P/NBB1QRKR w HFhf - perft 1 29 perft 2 728 perft 3 20768 @@ -326,7 +326,7 @@ perft 5 15621236 perft 6 415766465 id 36 -epd n2bqrkr/p1p1pppp/1pn5/3p1b2/P6P/1NP5/1P1PPPP1/1NBBQRKR w HFhf - 3 9 +epd n2bqrkr/p1p1pppp/1pn5/3p1b2/P6P/1NP5/1P1PPPP1/1NBBQRKR w HFhf - perft 1 20 perft 2 533 perft 3 12152 @@ -335,7 +335,7 @@ perft 5 8088751 perft 6 223068417 id 37 -epd nnbqrbkr/1pp1p1p1/p2p4/5p1p/2P1P3/N7/PPQP1PPP/N1B1RBKR w HEhe - 0 9 +epd nnbqrbkr/1pp1p1p1/p2p4/5p1p/2P1P3/N7/PPQP1PPP/N1B1RBKR w HEhe - perft 1 27 perft 2 619 perft 3 18098 @@ -344,7 +344,7 @@ perft 5 13755384 perft 6 357222394 id 38 -epd nnbqrkr1/pp1pp2p/2p2b2/5pp1/1P5P/4P1P1/P1PP1P2/NNBQRKRB w GEge - 1 9 +epd nnbqrkr1/pp1pp2p/2p2b2/5pp1/1P5P/4P1P1/P1PP1P2/NNBQRKRB w GEge - perft 1 32 perft 2 1046 perft 3 33721 @@ -353,7 +353,7 @@ perft 5 36218182 perft 6 1202830851 id 39 -epd nb1qbrkr/p1pppp2/1p1n2pp/8/1P6/2PN3P/P2PPPP1/NB1QBRKR w HFhf - 0 9 +epd nb1qbrkr/p1pppp2/1p1n2pp/8/1P6/2PN3P/P2PPPP1/NB1QBRKR w HFhf - perft 1 25 perft 2 521 perft 3 14021 @@ -362,7 +362,7 @@ perft 5 8697700 perft 6 201455191 id 40 -epd nnq1brkr/pp1pppp1/8/2p4P/8/5K2/PPPbPP1P/NNQBBR1R w hf - 0 9 +epd nnq1brkr/pp1pppp1/8/2p4P/8/5K2/PPPbPP1P/NNQBBR1R w hf - perft 1 23 perft 2 724 perft 3 18263 @@ -371,7 +371,7 @@ perft 5 15338230 perft 6 484638597 id 41 -epd nnqrbb1r/pppppk2/5pp1/7p/1P6/3P2PP/P1P1PP2/NNQRBBKR w HD - 0 9 +epd nnqrbb1r/pppppk2/5pp1/7p/1P6/3P2PP/P1P1PP2/NNQRBBKR w HD - perft 1 30 perft 2 717 perft 3 21945 @@ -380,7 +380,7 @@ perft 5 17166700 perft 6 450069742 id 42 -epd nnqr1krb/p1p1pppp/2bp4/8/1p1P4/4P3/PPP2PPP/NNQRBKRB w GDgd - 0 9 +epd nnqr1krb/p1p1pppp/2bp4/8/1p1P4/4P3/PPP2PPP/NNQRBKRB w GDgd - perft 1 25 perft 2 873 perft 3 20796 @@ -389,7 +389,7 @@ perft 5 18162741 perft 6 641708630 id 43 -epd nbnqrkbr/p2ppp2/1p4p1/2p4p/3P3P/3N4/PPP1PPPR/NB1QRKB1 w Ehe - 0 9 +epd nbnqrkbr/p2ppp2/1p4p1/2p4p/3P3P/3N4/PPP1PPPR/NB1QRKB1 w Ehe - perft 1 24 perft 2 589 perft 3 15190 @@ -398,7 +398,7 @@ perft 5 10630667 perft 6 279474189 id 44 -epd n1qbrkbr/p1ppp2p/2n2pp1/1p6/1P6/2P3P1/P2PPP1P/NNQBRKBR w HEhe - 0 9 +epd n1qbrkbr/p1ppp2p/2n2pp1/1p6/1P6/2P3P1/P2PPP1P/NNQBRKBR w HEhe - perft 1 22 perft 2 592 perft 3 14269 @@ -407,7 +407,7 @@ perft 5 10356818 perft 6 301583306 id 45 -epd 2qrkbbr/ppn1pppp/n1p5/3p4/5P2/P1PP4/1P2P1PP/NNQRKBBR w HDhd - 1 9 +epd 2qrkbbr/ppn1pppp/n1p5/3p4/5P2/P1PP4/1P2P1PP/NNQRKBBR w HDhd - perft 1 27 perft 2 750 perft 3 20584 @@ -416,7 +416,7 @@ perft 5 16819085 perft 6 516796736 id 46 -epd 1nqr1rbb/pppkp1pp/1n3p2/3p4/1P6/5P1P/P1PPPKP1/NNQR1RBB w - - 1 9 +epd 1nqr1rbb/pppkp1pp/1n3p2/3p4/1P6/5P1P/P1PPPKP1/NNQR1RBB w - - perft 1 24 perft 2 623 perft 3 15921 @@ -425,7 +425,7 @@ perft 5 11594634 perft 6 322745925 id 47 -epd bbn1rqkr/pp1pp2p/4npp1/2p5/1P6/2BPP3/P1P2PPP/1BNNRQKR w HEhe - 0 9 +epd bbn1rqkr/pp1pp2p/4npp1/2p5/1P6/2BPP3/P1P2PPP/1BNNRQKR w HEhe - perft 1 23 perft 2 730 perft 3 17743 @@ -434,7 +434,7 @@ perft 5 14496370 perft 6 468608864 id 48 -epd bn1brqkr/pppp2p1/3npp2/7p/PPP5/8/3PPPPP/BNNBRQKR w HEhe - 0 9 +epd bn1brqkr/pppp2p1/3npp2/7p/PPP5/8/3PPPPP/BNNBRQKR w HEhe - perft 1 25 perft 2 673 perft 3 17835 @@ -443,7 +443,7 @@ perft 5 14284338 perft 6 434008567 id 49 -epd bn1rqbkr/ppp1ppp1/1n6/2p4p/7P/3P4/PPP1PPP1/BN1RQBKR w HDhd - 0 9 +epd bn1rqbkr/ppp1ppp1/1n6/2p4p/7P/3P4/PPP1PPP1/BN1RQBKR w HDhd - perft 1 25 perft 2 776 perft 3 20562 @@ -452,7 +452,7 @@ perft 5 18486027 perft 6 616653869 id 50 -epd bnnr1krb/ppp2ppp/3p4/3Bp3/q1P3PP/8/PP1PPP2/BNNRQKR1 w GDgd - 0 9 +epd bnnr1krb/ppp2ppp/3p4/3Bp3/q1P3PP/8/PP1PPP2/BNNRQKR1 w GDgd - perft 1 29 perft 2 1040 perft 3 30772 @@ -461,7 +461,7 @@ perft 5 31801525 perft 6 1075147725 id 51 -epd 1bbnrqkr/pp1ppppp/8/2p5/n7/3PNPP1/PPP1P2P/NBB1RQKR w HEhe - 1 9 +epd 1bbnrqkr/pp1ppppp/8/2p5/n7/3PNPP1/PPP1P2P/NBB1RQKR w HEhe - perft 1 24 perft 2 598 perft 3 15673 @@ -470,7 +470,7 @@ perft 5 11394778 perft 6 310589129 id 52 -epd nnbbrqkr/p2ppp1p/1pp5/8/6p1/N1P5/PPBPPPPP/N1B1RQKR w HEhe - 0 9 +epd nnbbrqkr/p2ppp1p/1pp5/8/6p1/N1P5/PPBPPPPP/N1B1RQKR w HEhe - perft 1 26 perft 2 530 perft 3 14031 @@ -479,7 +479,7 @@ perft 5 8846766 perft 6 229270702 id 53 -epd nnbrqbkr/2p1p1pp/p4p2/1p1p4/8/NP6/P1PPPPPP/N1BRQBKR w HDhd - 0 9 +epd nnbrqbkr/2p1p1pp/p4p2/1p1p4/8/NP6/P1PPPPPP/N1BRQBKR w HDhd - perft 1 17 perft 2 496 perft 3 10220 @@ -488,7 +488,7 @@ perft 5 7103549 perft 6 217108001 id 54 -epd nnbrqk1b/pp2pprp/2pp2p1/8/3PP1P1/8/PPP2P1P/NNBRQRKB w d - 1 9 +epd nnbrqk1b/pp2pprp/2pp2p1/8/3PP1P1/8/PPP2P1P/NNBRQRKB w d - perft 1 33 perft 2 820 perft 3 27856 @@ -497,7 +497,7 @@ perft 5 24714401 perft 6 645835197 id 55 -epd 1bnrbqkr/ppnpp1p1/2p2p1p/8/1P6/4PPP1/P1PP3P/NBNRBQKR w HDhd - 0 9 +epd 1bnrbqkr/ppnpp1p1/2p2p1p/8/1P6/4PPP1/P1PP3P/NBNRBQKR w HDhd - perft 1 27 perft 2 705 perft 3 19760 @@ -506,7 +506,7 @@ perft 5 15964771 perft 6 464662032 id 56 -epd n1rbbqkr/pp1pppp1/7p/P1p5/1n6/2PP4/1P2PPPP/NNRBBQKR w HChc - 0 9 +epd n1rbbqkr/pp1pppp1/7p/P1p5/1n6/2PP4/1P2PPPP/NNRBBQKR w HChc - perft 1 22 perft 2 631 perft 3 14978 @@ -515,7 +515,7 @@ perft 5 10911545 perft 6 320838556 id 57 -epd n1rqb1kr/p1pppp1p/1pn4b/3P2p1/P7/1P6/2P1PPPP/NNRQBBKR w HChc - 0 9 +epd n1rqb1kr/p1pppp1p/1pn4b/3P2p1/P7/1P6/2P1PPPP/NNRQBBKR w HChc - perft 1 24 perft 2 477 perft 3 12506 @@ -524,7 +524,7 @@ perft 5 7419372 perft 6 165945904 id 58 -epd nnrqbkrb/pppp1pp1/7p/4p3/6P1/2N2B2/PPPPPP1P/NR1QBKR1 w Ggc - 2 9 +epd nnrqbkrb/pppp1pp1/7p/4p3/6P1/2N2B2/PPPPPP1P/NR1QBKR1 w Ggc - perft 1 29 perft 2 658 perft 3 19364 @@ -533,7 +533,7 @@ perft 5 14233587 perft 6 373744834 id 59 -epd n1nrqkbr/ppb2ppp/3pp3/2p5/2P3P1/5P2/PP1PPB1P/NBNRQK1R w HDhd - 1 9 +epd n1nrqkbr/ppb2ppp/3pp3/2p5/2P3P1/5P2/PP1PPB1P/NBNRQK1R w HDhd - perft 1 32 perft 2 801 perft 3 25861 @@ -542,7 +542,7 @@ perft 5 22318948 perft 6 619857455 id 60 -epd 2rbqkbr/p1pppppp/1nn5/1p6/7P/P4P2/1PPPP1PB/NNRBQK1R w HChc - 2 9 +epd 2rbqkbr/p1pppppp/1nn5/1p6/7P/P4P2/1PPPP1PB/NNRBQK1R w HChc - perft 1 27 perft 2 647 perft 3 18030 @@ -551,7 +551,7 @@ perft 5 13189156 perft 6 354689323 id 61 -epd nn1qkbbr/pp2ppp1/2rp4/2p4p/P2P4/1N5P/1PP1PPP1/1NRQKBBR w HCh - 1 9 +epd nn1qkbbr/pp2ppp1/2rp4/2p4p/P2P4/1N5P/1PP1PPP1/1NRQKBBR w HCh - perft 1 24 perft 2 738 perft 3 18916 @@ -560,7 +560,7 @@ perft 5 16420659 perft 6 519075930 id 62 -epd nnrqk1bb/p1ppp2p/5rp1/1p3p2/1P4P1/5P1P/P1PPP3/NNRQKRBB w FCc - 1 9 +epd nnrqk1bb/p1ppp2p/5rp1/1p3p2/1P4P1/5P1P/P1PPP3/NNRQKRBB w FCc - perft 1 25 perft 2 795 perft 3 20510 @@ -569,7 +569,7 @@ perft 5 17342527 perft 6 556144017 id 63 -epd bb1nrkqr/ppppn2p/4ppp1/8/1P4P1/4P3/P1PPKP1P/BBNNR1QR w he - 0 9 +epd bb1nrkqr/ppppn2p/4ppp1/8/1P4P1/4P3/P1PPKP1P/BBNNR1QR w he - perft 1 29 perft 2 664 perft 3 20024 @@ -578,7 +578,7 @@ perft 5 15373803 perft 6 406016364 id 64 -epd bnnbrkqr/1p1ppp2/8/p1p3pp/1P6/N4P2/PBPPP1PP/2NBRKQR w HEhe - 0 9 +epd bnnbrkqr/1p1ppp2/8/p1p3pp/1P6/N4P2/PBPPP1PP/2NBRKQR w HEhe - perft 1 31 perft 2 770 perft 3 24850 @@ -587,7 +587,7 @@ perft 5 22562080 perft 6 662029574 id 65 -epd 1nnrkbqr/p1pp1ppp/4p3/1p6/1Pb1P3/6PB/P1PP1P1P/BNNRK1QR w HDhd - 0 9 +epd 1nnrkbqr/p1pp1ppp/4p3/1p6/1Pb1P3/6PB/P1PP1P1P/BNNRK1QR w HDhd - perft 1 27 perft 2 776 perft 3 22133 @@ -596,7 +596,7 @@ perft 5 19153245 perft 6 562738257 id 66 -epd bnr1kqrb/pppp1pp1/1n5p/4p3/P3P3/3P2P1/1PP2P1P/BNNRKQRB w GDg - 0 9 +epd bnr1kqrb/pppp1pp1/1n5p/4p3/P3P3/3P2P1/1PP2P1P/BNNRKQRB w GDg - perft 1 26 perft 2 624 perft 3 16411 @@ -605,7 +605,7 @@ perft 5 11906515 perft 6 338092952 id 67 -epd nbbnrkqr/p1ppp1pp/1p3p2/8/2P5/4P3/PP1P1PPP/NBBNRKQR w HEhe - 1 9 +epd nbbnrkqr/p1ppp1pp/1p3p2/8/2P5/4P3/PP1P1PPP/NBBNRKQR w HEhe - perft 1 25 perft 2 624 perft 3 15561 @@ -614,7 +614,7 @@ perft 5 10817378 perft 6 311138112 id 68 -epd nn1brkqr/pp1bpppp/8/2pp4/P4P2/1PN5/2PPP1PP/N1BBRKQR w HEhe - 1 9 +epd nn1brkqr/pp1bpppp/8/2pp4/P4P2/1PN5/2PPP1PP/N1BBRKQR w HEhe - perft 1 23 perft 2 659 perft 3 16958 @@ -623,7 +623,7 @@ perft 5 13242252 perft 6 373557073 id 69 -epd n1brkbqr/ppp1pp1p/6pB/3p4/2Pn4/8/PP2PPPP/NN1RKBQR w HDhd - 0 9 +epd n1brkbqr/ppp1pp1p/6pB/3p4/2Pn4/8/PP2PPPP/NN1RKBQR w HDhd - perft 1 32 perft 2 1026 perft 3 30360 @@ -632,7 +632,7 @@ perft 5 29436320 perft 6 957904151 id 70 -epd nnbrkqrb/p2ppp2/Q5pp/1pp5/4PP2/2N5/PPPP2PP/N1BRK1RB w GDgd - 0 9 +epd nnbrkqrb/p2ppp2/Q5pp/1pp5/4PP2/2N5/PPPP2PP/N1BRK1RB w GDgd - perft 1 36 perft 2 843 perft 3 29017 @@ -641,7 +641,7 @@ perft 5 24321197 perft 6 630396940 id 71 -epd nbnrbk1r/pppppppq/8/7p/8/1N2QPP1/PPPPP2P/NB1RBK1R w HDhd - 2 9 +epd nbnrbk1r/pppppppq/8/7p/8/1N2QPP1/PPPPP2P/NB1RBK1R w HDhd - perft 1 36 perft 2 973 perft 3 35403 @@ -650,7 +650,7 @@ perft 5 37143354 perft 6 1124883780 id 72 -epd nnrbbkqr/2pppp1p/p7/6p1/1p2P3/4QPP1/PPPP3P/NNRBBK1R w HChc - 0 9 +epd nnrbbkqr/2pppp1p/p7/6p1/1p2P3/4QPP1/PPPP3P/NNRBBK1R w HChc - perft 1 36 perft 2 649 perft 3 22524 @@ -659,7 +659,7 @@ perft 5 16836636 perft 6 416139320 id 73 -epd nnrkbbqr/1p2pppp/p2p4/2p5/8/1N2P1P1/PPPP1P1P/1NKRBBQR w hc - 0 9 +epd nnrkbbqr/1p2pppp/p2p4/2p5/8/1N2P1P1/PPPP1P1P/1NKRBBQR w hc - perft 1 26 perft 2 672 perft 3 18136 @@ -668,7 +668,7 @@ perft 5 13342771 perft 6 363074681 id 74 -epd n1rkbqrb/pp1ppp2/2n3p1/2p4p/P5PP/1P6/2PPPP2/NNRKBQRB w GCgc - 0 9 +epd n1rkbqrb/pp1ppp2/2n3p1/2p4p/P5PP/1P6/2PPPP2/NNRKBQRB w GCgc - perft 1 24 perft 2 804 perft 3 20712 @@ -677,7 +677,7 @@ perft 5 18761475 perft 6 617932151 id 75 -epd nbkr1qbr/1pp1pppp/pn1p4/8/3P2P1/5R2/PPP1PP1P/NBN1KQBR w H - 2 9 +epd nbkr1qbr/1pp1pppp/pn1p4/8/3P2P1/5R2/PPP1PP1P/NBN1KQBR w H - perft 1 30 perft 2 627 perft 3 18669 @@ -686,7 +686,7 @@ perft 5 12815016 perft 6 312798696 id 76 -epd nnr1kqbr/pp1pp1p1/2p5/b4p1p/P7/1PNP4/2P1PPPP/N1RBKQBR w HChc - 1 9 +epd nnr1kqbr/pp1pp1p1/2p5/b4p1p/P7/1PNP4/2P1PPPP/N1RBKQBR w HChc - perft 1 12 perft 2 421 perft 3 6530 @@ -695,7 +695,7 @@ perft 5 4266410 perft 6 149176979 id 77 -epd n1rkqbbr/p1pp1pp1/np2p2p/8/8/N4PP1/PPPPP1BP/N1RKQ1BR w HChc - 0 9 +epd n1rkqbbr/p1pp1pp1/np2p2p/8/8/N4PP1/PPPPP1BP/N1RKQ1BR w HChc - perft 1 27 perft 2 670 perft 3 19119 @@ -704,7 +704,7 @@ perft 5 14708490 perft 6 397268628 id 78 -epd nnr1qrbb/p2kpppp/1p1p4/2p5/6P1/PP1P4/2P1PP1P/NNRKQRBB w FC - 0 9 +epd nnr1qrbb/p2kpppp/1p1p4/2p5/6P1/PP1P4/2P1PP1P/NNRKQRBB w FC - perft 1 27 perft 2 604 perft 3 17043 @@ -713,7 +713,7 @@ perft 5 11993332 perft 6 308518181 id 79 -epd bbnnrkrq/ppp1pp2/6p1/3p4/7p/7P/PPPPPPP1/BBNNRRKQ w ge - 0 9 +epd bbnnrkrq/ppp1pp2/6p1/3p4/7p/7P/PPPPPPP1/BBNNRRKQ w ge - perft 1 20 perft 2 559 perft 3 12242 @@ -722,7 +722,7 @@ perft 5 8427161 perft 6 252274233 id 80 -epd bnnbrkr1/ppp2p1p/5q2/3pp1p1/4P3/1N4P1/PPPPRP1P/BN1B1KRQ w Gge - 0 9 +epd bnnbrkr1/ppp2p1p/5q2/3pp1p1/4P3/1N4P1/PPPPRP1P/BN1B1KRQ w Gge - perft 1 26 perft 2 1036 perft 3 27228 @@ -731,7 +731,7 @@ perft 5 28286576 perft 6 1042120495 id 81 -epd bn1rkbrq/1pppppp1/p6p/1n6/3P4/6PP/PPPRPP2/BNN1KBRQ w Ggd - 2 9 +epd bn1rkbrq/1pppppp1/p6p/1n6/3P4/6PP/PPPRPP2/BNN1KBRQ w Ggd - perft 1 29 perft 2 633 perft 3 19278 @@ -740,7 +740,7 @@ perft 5 14333034 perft 6 361900466 id 82 -epd b1nrkrqb/1p1npppp/p2p4/2p5/5P2/4P2P/PPPP1RP1/BNNRK1QB w Dfd - 1 9 +epd b1nrkrqb/1p1npppp/p2p4/2p5/5P2/4P2P/PPPP1RP1/BNNRK1QB w Dfd - perft 1 25 perft 2 475 perft 3 12603 @@ -749,7 +749,7 @@ perft 5 7545536 perft 6 179579818 id 83 -epd 1bbnrkrq/ppppppp1/8/7p/1n4P1/1PN5/P1PPPP1P/NBBR1KRQ w Gge - 0 9 +epd 1bbnrkrq/ppppppp1/8/7p/1n4P1/1PN5/P1PPPP1P/NBBR1KRQ w Gge - perft 1 30 perft 2 803 perft 3 25473 @@ -758,7 +758,7 @@ perft 5 23443854 perft 6 686365049 id 84 -epd nnbbrkrq/2pp1pp1/1p5p/pP2p3/7P/N7/P1PPPPP1/N1BBRKRQ w GEge - 0 9 +epd nnbbrkrq/2pp1pp1/1p5p/pP2p3/7P/N7/P1PPPPP1/N1BBRKRQ w GEge - perft 1 18 perft 2 432 perft 3 9638 @@ -767,7 +767,7 @@ perft 5 6131124 perft 6 160393505 id 85 -epd nnbrkbrq/1pppp1p1/p7/7p/1P2Pp2/BN6/P1PP1PPP/1N1RKBRQ w GDgd - 0 9 +epd nnbrkbrq/1pppp1p1/p7/7p/1P2Pp2/BN6/P1PP1PPP/1N1RKBRQ w GDgd - perft 1 27 perft 2 482 perft 3 13441 @@ -776,7 +776,7 @@ perft 5 8084701 perft 6 193484216 id 86 -epd n1brkrqb/pppp3p/n3pp2/6p1/3P1P2/N1P5/PP2P1PP/N1BRKRQB w FDfd - 0 9 +epd n1brkrqb/pppp3p/n3pp2/6p1/3P1P2/N1P5/PP2P1PP/N1BRKRQB w FDfd - perft 1 28 perft 2 642 perft 3 19005 @@ -785,7 +785,7 @@ perft 5 14529434 perft 6 384837696 id 87 -epd nbnrbk2/p1pppp1p/1p3qr1/6p1/1B1P4/1N6/PPP1PPPP/1BNR1RKQ w d - 2 9 +epd nbnrbk2/p1pppp1p/1p3qr1/6p1/1B1P4/1N6/PPP1PPPP/1BNR1RKQ w d - perft 1 30 perft 2 796 perft 3 22780 @@ -794,7 +794,7 @@ perft 5 20120565 perft 6 641832725 id 88 -epd nnrbbrkq/1pp2ppp/3p4/p3p3/3P1P2/1P2P3/P1P3PP/NNRBBKRQ w GC - 1 9 +epd nnrbbrkq/1pp2ppp/3p4/p3p3/3P1P2/1P2P3/P1P3PP/NNRBBKRQ w GC - perft 1 31 perft 2 827 perft 3 24538 @@ -803,7 +803,7 @@ perft 5 19979594 perft 6 549437308 id 89 -epd nnrkbbrq/1pp2p1p/p2pp1p1/2P5/8/8/PP1PPPPP/NNRKBBRQ w Ggc - 0 9 +epd nnrkbbrq/1pp2p1p/p2pp1p1/2P5/8/8/PP1PPPPP/NNRKBBRQ w Ggc - perft 1 24 perft 2 762 perft 3 19283 @@ -812,7 +812,7 @@ perft 5 16838099 perft 6 555230555 id 90 -epd nnr1brqb/1ppkp1pp/8/p2p1p2/1P1P4/N1P5/P3PPPP/N1RKBRQB w FC - 1 9 +epd nnr1brqb/1ppkp1pp/8/p2p1p2/1P1P4/N1P5/P3PPPP/N1RKBRQB w FC - perft 1 23 perft 2 640 perft 3 15471 @@ -821,7 +821,7 @@ perft 5 11343507 perft 6 334123513 id 91 -epd nbnrkrbq/2ppp2p/p4p2/1P4p1/4PP2/8/1PPP2PP/NBNRKRBQ w FDfd - 0 9 +epd nbnrkrbq/2ppp2p/p4p2/1P4p1/4PP2/8/1PPP2PP/NBNRKRBQ w FDfd - perft 1 31 perft 2 826 perft 3 26137 @@ -830,7 +830,7 @@ perft 5 23555139 perft 6 686250413 id 92 -epd 1nrbkr1q/1pppp1pp/1n6/p4p2/N1b4P/8/PPPPPPPB/N1RBKR1Q w FCfc - 2 9 +epd 1nrbkr1q/1pppp1pp/1n6/p4p2/N1b4P/8/PPPPPPPB/N1RBKR1Q w FCfc - perft 1 27 perft 2 862 perft 3 24141 @@ -839,7 +839,7 @@ perft 5 22027695 perft 6 696353497 id 93 -epd nnrkrbbq/pppp2pp/8/4pp2/4P3/P7/1PPPBPPP/NNKRR1BQ w c - 0 9 +epd nnrkrbbq/pppp2pp/8/4pp2/4P3/P7/1PPPBPPP/NNKRR1BQ w c - perft 1 25 perft 2 792 perft 3 19883 @@ -848,7 +848,7 @@ perft 5 16473376 perft 6 532214177 id 94 -epd n1rk1qbb/pppprpp1/2n4p/4p3/2PP3P/8/PP2PPP1/NNRKRQBB w ECc - 1 9 +epd n1rk1qbb/pppprpp1/2n4p/4p3/2PP3P/8/PP2PPP1/NNRKRQBB w ECc - perft 1 25 perft 2 622 perft 3 16031 @@ -857,7 +857,7 @@ perft 5 11420973 perft 6 321855685 id 95 -epd bbq1rnkr/pnp1pp1p/1p1p4/6p1/2P5/2Q1P2P/PP1P1PP1/BB1NRNKR w HEhe - 2 9 +epd bbq1rnkr/pnp1pp1p/1p1p4/6p1/2P5/2Q1P2P/PP1P1PP1/BB1NRNKR w HEhe - perft 1 36 perft 2 870 perft 3 30516 @@ -866,7 +866,7 @@ perft 5 28127620 perft 6 799738334 id 96 -epd bq1brnkr/1p1ppp1p/1np5/p5p1/8/1N5P/PPPPPPP1/BQ1BRNKR w HEhe - 0 9 +epd bq1brnkr/1p1ppp1p/1np5/p5p1/8/1N5P/PPPPPPP1/BQ1BRNKR w HEhe - perft 1 22 perft 2 588 perft 3 13524 @@ -875,7 +875,7 @@ perft 5 9359618 perft 6 273795898 id 97 -epd bq1rn1kr/1pppppbp/Nn4p1/8/8/P7/1PPPPPPP/BQ1RNBKR w HDhd - 1 9 +epd bq1rn1kr/1pppppbp/Nn4p1/8/8/P7/1PPPPPPP/BQ1RNBKR w HDhd - perft 1 24 perft 2 711 perft 3 18197 @@ -884,7 +884,7 @@ perft 5 14692779 perft 6 445827351 id 98 -epd bqnr1kr1/pppppp1p/6p1/5n2/4B3/3N2PP/PbPPPP2/BQNR1KR1 w GDgd - 2 9 +epd bqnr1kr1/pppppp1p/6p1/5n2/4B3/3N2PP/PbPPPP2/BQNR1KR1 w GDgd - perft 1 31 perft 2 1132 perft 3 36559 @@ -893,7 +893,7 @@ perft 5 43256823 perft 6 1456721391 id 99 -epd qbb1rnkr/ppp3pp/4n3/3ppp2/1P3PP1/8/P1PPPN1P/QBB1RNKR w HEhe - 0 9 +epd qbb1rnkr/ppp3pp/4n3/3ppp2/1P3PP1/8/P1PPPN1P/QBB1RNKR w HEhe - perft 1 28 perft 2 696 perft 3 20502 @@ -902,7 +902,7 @@ perft 5 16492398 perft 6 456983120 id 100 -epd qnbbr1kr/pp1ppp1p/4n3/6p1/2p3P1/2PP1P2/PP2P2P/QNBBRNKR w HEhe - 0 9 +epd qnbbr1kr/pp1ppp1p/4n3/6p1/2p3P1/2PP1P2/PP2P2P/QNBBRNKR w HEhe - perft 1 25 perft 2 655 perft 3 16520 @@ -911,7 +911,7 @@ perft 5 11767038 perft 6 335414976 id 101 -epd 1nbrnbkr/p1ppp1pp/1p6/5p2/4q1PP/3P4/PPP1PP2/QNBRNBKR w HDhd - 1 9 +epd 1nbrnbkr/p1ppp1pp/1p6/5p2/4q1PP/3P4/PPP1PP2/QNBRNBKR w HDhd - perft 1 30 perft 2 1162 perft 3 33199 @@ -920,7 +920,7 @@ perft 5 36048727 perft 6 1290346802 id 102 -epd q1brnkrb/p1pppppp/n7/1p6/P7/3P1P2/QPP1P1PP/1NBRNKRB w GDgd - 0 9 +epd q1brnkrb/p1pppppp/n7/1p6/P7/3P1P2/QPP1P1PP/1NBRNKRB w GDgd - perft 1 32 perft 2 827 perft 3 26106 @@ -929,7 +929,7 @@ perft 5 23143989 perft 6 673147648 id 103 -epd qbnrb1kr/ppp1pp1p/3p4/2n3p1/1P6/6N1/P1PPPPPP/QBNRB1KR w HDhd - 2 9 +epd qbnrb1kr/ppp1pp1p/3p4/2n3p1/1P6/6N1/P1PPPPPP/QBNRB1KR w HDhd - perft 1 29 perft 2 751 perft 3 23132 @@ -938,7 +938,7 @@ perft 5 19555214 perft 6 530475036 id 104 -epd q1rbbnkr/pppp1p2/2n3pp/2P1p3/3P4/8/PP1NPPPP/Q1RBBNKR w HChc - 2 9 +epd q1rbbnkr/pppp1p2/2n3pp/2P1p3/3P4/8/PP1NPPPP/Q1RBBNKR w HChc - perft 1 29 perft 2 806 perft 3 24540 @@ -947,7 +947,7 @@ perft 5 21694330 perft 6 619907316 id 105 -epd q1r1bbkr/pnpp1ppp/2n1p3/1p6/2P2P2/2N1N3/PP1PP1PP/Q1R1BBKR w HChc - 2 9 +epd q1r1bbkr/pnpp1ppp/2n1p3/1p6/2P2P2/2N1N3/PP1PP1PP/Q1R1BBKR w HChc - perft 1 32 perft 2 1017 perft 3 32098 @@ -956,7 +956,7 @@ perft 5 31204371 perft 6 958455898 id 106 -epd 2rnbkrb/pqppppp1/1pn5/7p/2P5/P1R5/QP1PPPPP/1N1NBKRB w Ggc - 4 9 +epd 2rnbkrb/pqppppp1/1pn5/7p/2P5/P1R5/QP1PPPPP/1N1NBKRB w Ggc - perft 1 26 perft 2 625 perft 3 16506 @@ -965,7 +965,7 @@ perft 5 11856964 perft 6 336672890 id 107 -epd qbnr1kbr/p2ppppp/2p5/1p6/4n2P/P4N2/1PPP1PP1/QBNR1KBR w HDhd - 0 9 +epd qbnr1kbr/p2ppppp/2p5/1p6/4n2P/P4N2/1PPP1PP1/QBNR1KBR w HDhd - perft 1 27 perft 2 885 perft 3 23828 @@ -974,7 +974,7 @@ perft 5 21855658 perft 6 706272554 id 108 -epd qnrbnk1r/pp1pp2p/5p2/2pbP1p1/3P4/1P6/P1P2PPP/QNRBNKBR w HChc - 0 9 +epd qnrbnk1r/pp1pp2p/5p2/2pbP1p1/3P4/1P6/P1P2PPP/QNRBNKBR w HChc - perft 1 26 perft 2 954 perft 3 24832 @@ -983,7 +983,7 @@ perft 5 24415089 perft 6 866744329 id 109 -epd qnrnk1br/p1p2ppp/8/1pbpp3/8/PP2N3/1QPPPPPP/1NR1KBBR w HChc - 0 9 +epd qnrnk1br/p1p2ppp/8/1pbpp3/8/PP2N3/1QPPPPPP/1NR1KBBR w HChc - perft 1 26 perft 2 783 perft 3 20828 @@ -992,7 +992,7 @@ perft 5 17477825 perft 6 539674275 id 110 -epd qnrnkrbb/Bpppp2p/6p1/5p2/5P2/3PP3/PPP3PP/QNRNKR1B w FCfc - 1 9 +epd qnrnkrbb/Bpppp2p/6p1/5p2/5P2/3PP3/PPP3PP/QNRNKR1B w FCfc - perft 1 28 perft 2 908 perft 3 25730 @@ -1001,7 +1001,7 @@ perft 5 25251641 perft 6 869525254 id 111 -epd bbnqrn1r/ppppp2k/5p2/6pp/7P/1QP5/PP1PPPP1/B1N1RNKR w HE - 0 9 +epd bbnqrn1r/ppppp2k/5p2/6pp/7P/1QP5/PP1PPPP1/B1N1RNKR w HE - perft 1 33 perft 2 643 perft 3 21790 @@ -1010,7 +1010,7 @@ perft 5 16693640 perft 6 410115900 id 112 -epd b1qbrnkr/ppp1pp2/2np4/6pp/4P3/2N4P/PPPP1PP1/BQ1BRNKR w HEhe - 0 9 +epd b1qbrnkr/ppp1pp2/2np4/6pp/4P3/2N4P/PPPP1PP1/BQ1BRNKR w HEhe - perft 1 28 perft 2 837 perft 3 24253 @@ -1019,7 +1019,7 @@ perft 5 22197063 perft 6 696399065 id 113 -epd bnqr1bkr/pp1ppppp/2p5/4N3/5P2/P7/1PPPPnPP/BNQR1BKR w HDhd - 3 9 +epd bnqr1bkr/pp1ppppp/2p5/4N3/5P2/P7/1PPPPnPP/BNQR1BKR w HDhd - perft 1 25 perft 2 579 perft 3 13909 @@ -1028,7 +1028,7 @@ perft 5 8601011 perft 6 225530258 id 114 -epd b1qr1krb/pp1ppppp/n2n4/8/2p5/2P3P1/PP1PPP1P/BNQRNKRB w GDgd - 0 9 +epd b1qr1krb/pp1ppppp/n2n4/8/2p5/2P3P1/PP1PPP1P/BNQRNKRB w GDgd - perft 1 28 perft 2 707 perft 3 19721 @@ -1037,7 +1037,7 @@ perft 5 15583376 perft 6 468399900 id 115 -epd nbbqr1kr/1pppp1pp/8/p1n2p2/4P3/PN6/1PPPQPPP/1BB1RNKR w HEhe - 0 9 +epd nbbqr1kr/1pppp1pp/8/p1n2p2/4P3/PN6/1PPPQPPP/1BB1RNKR w HEhe - perft 1 30 perft 2 745 perft 3 23416 @@ -1046,7 +1046,7 @@ perft 5 19478789 perft 6 515473678 id 116 -epd nqbbrn1r/p1pppp1k/1p4p1/7p/4P3/1R3B2/PPPP1PPP/NQB2NKR w H - 0 9 +epd nqbbrn1r/p1pppp1k/1p4p1/7p/4P3/1R3B2/PPPP1PPP/NQB2NKR w H - perft 1 24 perft 2 504 perft 3 13512 @@ -1055,7 +1055,7 @@ perft 5 9002073 perft 6 228726497 id 117 -epd nqbr1bkr/p1p1ppp1/1p1n4/3pN2p/1P6/8/P1PPPPPP/NQBR1BKR w HDhd - 0 9 +epd nqbr1bkr/p1p1ppp1/1p1n4/3pN2p/1P6/8/P1PPPPPP/NQBR1BKR w HDhd - perft 1 29 perft 2 898 perft 3 26532 @@ -1064,7 +1064,7 @@ perft 5 24703467 perft 6 757166494 id 118 -epd nqbrn1rb/pppp1kp1/5p1p/4p3/P4B2/3P2P1/1PP1PP1P/NQ1RNKRB w GD - 0 9 +epd nqbrn1rb/pppp1kp1/5p1p/4p3/P4B2/3P2P1/1PP1PP1P/NQ1RNKRB w GD - perft 1 34 perft 2 671 perft 3 22332 @@ -1073,7 +1073,7 @@ perft 5 15556806 perft 6 353235120 id 119 -epd nb1r1nkr/ppp1ppp1/2bp4/7p/3P2qP/P6R/1PP1PPP1/NBQRBNK1 w Dhd - 1 9 +epd nb1r1nkr/ppp1ppp1/2bp4/7p/3P2qP/P6R/1PP1PPP1/NBQRBNK1 w Dhd - perft 1 38 perft 2 1691 perft 3 60060 @@ -1082,7 +1082,7 @@ perft 5 88557078 perft 6 3589649998 id 120 -epd n1rbbnkr/1p1pp1pp/p7/2p1qp2/1B3P2/3P4/PPP1P1PP/NQRB1NKR w HChc - 0 9 +epd n1rbbnkr/1p1pp1pp/p7/2p1qp2/1B3P2/3P4/PPP1P1PP/NQRB1NKR w HChc - perft 1 24 perft 2 913 perft 3 21595 @@ -1091,7 +1091,7 @@ perft 5 19866918 perft 6 737239330 id 121 -epd nqrnbbkr/p2p1p1p/1pp5/1B2p1p1/1P3P2/4P3/P1PP2PP/NQRNB1KR w HChc - 0 9 +epd nqrnbbkr/p2p1p1p/1pp5/1B2p1p1/1P3P2/4P3/P1PP2PP/NQRNB1KR w HChc - perft 1 33 perft 2 913 perft 3 30159 @@ -1100,7 +1100,7 @@ perft 5 28053260 perft 6 804687975 id 122 -epd nqr1bkrb/ppp1pp2/2np2p1/P6p/8/2P4P/1P1PPPP1/NQRNBKRB w GCgc - 0 9 +epd nqr1bkrb/ppp1pp2/2np2p1/P6p/8/2P4P/1P1PPPP1/NQRNBKRB w GCgc - perft 1 24 perft 2 623 perft 3 16569 @@ -1109,7 +1109,7 @@ perft 5 12681936 perft 6 351623879 id 123 -epd nb1rnkbr/pqppppp1/1p5p/8/1PP4P/8/P2PPPP1/NBQRNKBR w HDhd - 1 9 +epd nb1rnkbr/pqppppp1/1p5p/8/1PP4P/8/P2PPPP1/NBQRNKBR w HDhd - perft 1 31 perft 2 798 perft 3 24862 @@ -1118,7 +1118,7 @@ perft 5 22616076 perft 6 666227466 id 124 -epd nqrbnkbr/2p1p1pp/3p4/pp3p2/6PP/3P1N2/PPP1PP2/NQRB1KBR w HChc - 0 9 +epd nqrbnkbr/2p1p1pp/3p4/pp3p2/6PP/3P1N2/PPP1PP2/NQRB1KBR w HChc - perft 1 24 perft 2 590 perft 3 14409 @@ -1127,7 +1127,7 @@ perft 5 9698432 perft 6 274064911 id 125 -epd nqrnkbbr/pp1p1p1p/4p1p1/1p6/8/5P1P/P1PPP1P1/NQRNKBBR w HChc - 0 9 +epd nqrnkbbr/pp1p1p1p/4p1p1/1p6/8/5P1P/P1PPP1P1/NQRNKBBR w HChc - perft 1 30 perft 2 1032 perft 3 31481 @@ -1136,7 +1136,7 @@ perft 5 34914919 perft 6 1233362066 id 126 -epd nqrnkrbb/p2ppppp/1p6/2p5/2P3P1/5P2/PP1PPN1P/NQR1KRBB w FCfc - 1 9 +epd nqrnkrbb/p2ppppp/1p6/2p5/2P3P1/5P2/PP1PPN1P/NQR1KRBB w FCfc - perft 1 30 perft 2 775 perft 3 23958 @@ -1145,7 +1145,7 @@ perft 5 21141738 perft 6 621142773 id 127 -epd bbnrqrk1/pp2pppp/4n3/2pp4/P7/1N5P/BPPPPPP1/B2RQNKR w HD - 2 9 +epd bbnrqrk1/pp2pppp/4n3/2pp4/P7/1N5P/BPPPPPP1/B2RQNKR w HD - perft 1 23 perft 2 708 perft 3 17164 @@ -1154,7 +1154,7 @@ perft 5 14343443 perft 6 481405144 id 128 -epd bnr1qnkr/p1pp1p1p/1p4p1/4p1b1/2P1P3/1P6/PB1P1PPP/1NRBQNKR w HChc - 1 9 +epd bnr1qnkr/p1pp1p1p/1p4p1/4p1b1/2P1P3/1P6/PB1P1PPP/1NRBQNKR w HChc - perft 1 30 perft 2 931 perft 3 29249 @@ -1163,7 +1163,7 @@ perft 5 30026687 perft 6 968109774 id 129 -epd b1rqnbkr/ppp1ppp1/3p3p/2n5/P3P3/2NP4/1PP2PPP/B1RQNBKR w HChc - 0 9 +epd b1rqnbkr/ppp1ppp1/3p3p/2n5/P3P3/2NP4/1PP2PPP/B1RQNBKR w HChc - perft 1 24 perft 2 596 perft 3 15533 @@ -1172,7 +1172,7 @@ perft 5 11099382 perft 6 294180723 id 130 -epd bnrqnr1b/pp1pkppp/2p1p3/P7/2P5/7P/1P1PPPP1/BNRQNKRB w GC - 0 9 +epd bnrqnr1b/pp1pkppp/2p1p3/P7/2P5/7P/1P1PPPP1/BNRQNKRB w GC - perft 1 24 perft 2 572 perft 3 15293 @@ -1181,7 +1181,7 @@ perft 5 11208688 perft 6 302955778 id 131 -epd n1brq1kr/bppppppp/p7/8/4P1Pn/8/PPPP1P2/NBBRQNKR w HDhd - 0 9 +epd n1brq1kr/bppppppp/p7/8/4P1Pn/8/PPPP1P2/NBBRQNKR w HDhd - perft 1 20 perft 2 570 perft 3 13139 @@ -1190,7 +1190,7 @@ perft 5 9919113 perft 6 284592289 id 132 -epd 1rbbqnkr/ppn1ppp1/3p3p/2p5/3P4/1N4P1/PPPBPP1P/1R1BQNKR w HBhb - 0 9 +epd 1rbbqnkr/ppn1ppp1/3p3p/2p5/3P4/1N4P1/PPPBPP1P/1R1BQNKR w HBhb - perft 1 29 perft 2 1009 perft 3 29547 @@ -1199,7 +1199,7 @@ perft 5 31059587 perft 6 1111986835 id 133 -epd nrbq2kr/ppppppb1/5n1p/5Pp1/8/P5P1/1PPPP2P/NRBQNBKR w HBhb - 1 9 +epd nrbq2kr/ppppppb1/5n1p/5Pp1/8/P5P1/1PPPP2P/NRBQNBKR w HBhb - perft 1 20 perft 2 520 perft 3 11745 @@ -1208,7 +1208,7 @@ perft 5 7809837 perft 6 216997152 id 134 -epd nrb1nkrb/pp3ppp/1qBpp3/2p5/8/P5P1/1PPPPP1P/NRBQNKR1 w GBgb - 2 9 +epd nrb1nkrb/pp3ppp/1qBpp3/2p5/8/P5P1/1PPPPP1P/NRBQNKR1 w GBgb - perft 1 32 perft 2 850 perft 3 25642 @@ -1217,7 +1217,7 @@ perft 5 21981567 perft 6 664886187 id 135 -epd 1br1bnkr/ppqppp1p/1np3p1/8/1PP4P/4N3/P2PPPP1/NBRQB1KR w HChc - 1 9 +epd 1br1bnkr/ppqppp1p/1np3p1/8/1PP4P/4N3/P2PPPP1/NBRQB1KR w HChc - perft 1 32 perft 2 798 perft 3 24765 @@ -1226,7 +1226,7 @@ perft 5 22076141 perft 6 670296871 id 136 -epd nrqbb1kr/1p1pp1pp/2p3n1/p4p2/3PP3/P5N1/1PP2PPP/NRQBB1KR w HBhb - 0 9 +epd nrqbb1kr/1p1pp1pp/2p3n1/p4p2/3PP3/P5N1/1PP2PPP/NRQBB1KR w HBhb - perft 1 32 perft 2 791 perft 3 26213 @@ -1235,7 +1235,7 @@ perft 5 23239122 perft 6 634260266 id 137 -epd nrqn1bkr/ppppp1pp/4b3/8/4P1p1/5P2/PPPP3P/NRQNBBKR w HBhb - 0 9 +epd nrqn1bkr/ppppp1pp/4b3/8/4P1p1/5P2/PPPP3P/NRQNBBKR w HBhb - perft 1 29 perft 2 687 perft 3 20223 @@ -1244,7 +1244,7 @@ perft 5 15236287 perft 6 398759980 id 138 -epd nrqnbrkb/pppp1p2/4p2p/3B2p1/8/1P4P1/PQPPPP1P/NR1NBKR1 w GB - 0 9 +epd nrqnbrkb/pppp1p2/4p2p/3B2p1/8/1P4P1/PQPPPP1P/NR1NBKR1 w GB - perft 1 37 perft 2 764 perft 3 27073 @@ -1253,7 +1253,7 @@ perft 5 21284835 perft 6 514864869 id 139 -epd nbrq1kbr/Bp3ppp/2pnp3/3p4/5P2/2P4P/PP1PP1P1/NBRQNK1R w HChc - 0 9 +epd nbrq1kbr/Bp3ppp/2pnp3/3p4/5P2/2P4P/PP1PP1P1/NBRQNK1R w HChc - perft 1 40 perft 2 1271 perft 3 48022 @@ -1262,7 +1262,7 @@ perft 5 56588117 perft 6 1850696281 id 140 -epd nrqbnkbr/1p2ppp1/p1p4p/3p4/1P6/8/PQPPPPPP/1RNBNKBR w HBhb - 0 9 +epd nrqbnkbr/1p2ppp1/p1p4p/3p4/1P6/8/PQPPPPPP/1RNBNKBR w HBhb - perft 1 28 perft 2 757 perft 3 23135 @@ -1271,7 +1271,7 @@ perft 5 21427496 perft 6 650939962 id 141 -epd nrqn1bbr/2ppkppp/4p3/pB6/8/2P1P3/PP1P1PPP/NRQNK1BR w HB - 1 9 +epd nrqn1bbr/2ppkppp/4p3/pB6/8/2P1P3/PP1P1PPP/NRQNK1BR w HB - perft 1 27 perft 2 642 perft 3 17096 @@ -1280,7 +1280,7 @@ perft 5 11872805 perft 6 327545120 id 142 -epd nrqnkrb1/p1ppp2p/1p4p1/4bp2/4PP1P/4N3/PPPP2P1/NRQ1KRBB w FBfb - 1 9 +epd nrqnkrb1/p1ppp2p/1p4p1/4bp2/4PP1P/4N3/PPPP2P1/NRQ1KRBB w FBfb - perft 1 27 perft 2 958 perft 3 27397 @@ -1289,7 +1289,7 @@ perft 5 28520172 perft 6 995356563 id 143 -epd 1bnrnqkr/pbpp2pp/8/1p2pp2/P6P/3P1N2/1PP1PPP1/BBNR1QKR w HDhd - 0 9 +epd 1bnrnqkr/pbpp2pp/8/1p2pp2/P6P/3P1N2/1PP1PPP1/BBNR1QKR w HDhd - perft 1 27 perft 2 859 perft 3 23475 @@ -1298,7 +1298,7 @@ perft 5 21581178 perft 6 732696327 id 144 -epd b1rbnqkr/1pp1ppp1/2n4p/p2p4/5P2/1PBP4/P1P1P1PP/1NRBNQKR w HChc - 0 9 +epd b1rbnqkr/1pp1ppp1/2n4p/p2p4/5P2/1PBP4/P1P1P1PP/1NRBNQKR w HChc - perft 1 26 perft 2 545 perft 3 14817 @@ -1307,7 +1307,7 @@ perft 5 9537260 perft 6 233549184 id 145 -epd 1nrnqbkr/p1pppppp/1p6/8/2b2P2/P1N5/1PP1P1PP/BNR1QBKR w HChc - 2 9 +epd 1nrnqbkr/p1pppppp/1p6/8/2b2P2/P1N5/1PP1P1PP/BNR1QBKR w HChc - perft 1 24 perft 2 668 perft 3 17716 @@ -1316,7 +1316,7 @@ perft 5 14216070 perft 6 406225409 id 146 -epd 1nrnqkrb/2ppp1pp/p7/1p3p2/5P2/N5K1/PPPPP2P/B1RNQ1RB w gc - 0 9 +epd 1nrnqkrb/2ppp1pp/p7/1p3p2/5P2/N5K1/PPPPP2P/B1RNQ1RB w gc - perft 1 33 perft 2 725 perft 3 23572 @@ -1325,7 +1325,7 @@ perft 5 18547476 perft 6 471443091 id 147 -epd nbbr1qkr/p1pppppp/8/1p1n4/3P4/1N3PP1/PPP1P2P/1BBRNQKR w HDhd - 1 9 +epd nbbr1qkr/p1pppppp/8/1p1n4/3P4/1N3PP1/PPP1P2P/1BBRNQKR w HDhd - perft 1 28 perft 2 698 perft 3 20527 @@ -1334,7 +1334,7 @@ perft 5 16555068 perft 6 458045505 id 148 -epd 1rbbnqkr/1pnppp1p/p5p1/2p5/2P4P/5P2/PP1PP1PR/NRBBNQK1 w Bhb - 1 9 +epd 1rbbnqkr/1pnppp1p/p5p1/2p5/2P4P/5P2/PP1PP1PR/NRBBNQK1 w Bhb - perft 1 24 perft 2 554 perft 3 14221 @@ -1343,7 +1343,7 @@ perft 5 9863080 perft 6 269284081 id 149 -epd nrb1qbkr/2pppppp/2n5/p7/2p5/4P3/PPNP1PPP/1RBNQBKR w HBhb - 0 9 +epd nrb1qbkr/2pppppp/2n5/p7/2p5/4P3/PPNP1PPP/1RBNQBKR w HBhb - perft 1 23 perft 2 618 perft 3 15572 @@ -1352,7 +1352,7 @@ perft 5 12044358 perft 6 360311412 id 150 -epd nrb1qkrb/2ppppp1/p3n3/1p1B3p/2P5/6P1/PP1PPPRP/NRBNQK2 w Bgb - 2 9 +epd nrb1qkrb/2ppppp1/p3n3/1p1B3p/2P5/6P1/PP1PPPRP/NRBNQK2 w Bgb - perft 1 27 perft 2 593 perft 3 16770 @@ -1361,7 +1361,7 @@ perft 5 11806808 perft 6 303338935 id 151 -epd nbrn1qkr/ppp1pp2/3p2p1/3Q3P/b7/8/PPPPPP1P/NBRNB1KR w HChc - 2 9 +epd nbrn1qkr/ppp1pp2/3p2p1/3Q3P/b7/8/PPPPPP1P/NBRNB1KR w HChc - perft 1 39 perft 2 1056 perft 3 40157 @@ -1370,7 +1370,7 @@ perft 5 42201531 perft 6 1239888683 id 152 -epd nr1bbqkr/pp1pp2p/1n3pp1/2p5/8/1P4P1/P1PPPPQP/NRNBBK1R w hb - 0 9 +epd nr1bbqkr/pp1pp2p/1n3pp1/2p5/8/1P4P1/P1PPPPQP/NRNBBK1R w hb - perft 1 25 perft 2 585 perft 3 15719 @@ -1379,7 +1379,7 @@ perft 5 11582539 perft 6 320997679 id 153 -epd nr2bbkr/ppp1pppp/1n1p4/8/6PP/1NP4q/PP1PPP2/1RNQBBKR w HBhb - 1 9 +epd nr2bbkr/ppp1pppp/1n1p4/8/6PP/1NP4q/PP1PPP2/1RNQBBKR w HBhb - perft 1 22 perft 2 742 perft 3 15984 @@ -1388,7 +1388,7 @@ perft 5 13287051 perft 6 457010195 id 154 -epd 1rnqbkrb/ppp1p1p1/1n3p2/3p3p/P6P/4P3/1PPP1PP1/NRNQBRKB w gb - 0 9 +epd 1rnqbkrb/ppp1p1p1/1n3p2/3p3p/P6P/4P3/1PPP1PP1/NRNQBRKB w gb - perft 1 22 perft 2 574 perft 3 14044 @@ -1397,7 +1397,7 @@ perft 5 9968830 perft 6 281344367 id 155 -epd nb1rqkbr/1pppp1pp/4n3/p4p2/6PP/5P2/PPPPPN2/NBR1QKBR w HCh - 0 9 +epd nb1rqkbr/1pppp1pp/4n3/p4p2/6PP/5P2/PPPPPN2/NBR1QKBR w HCh - perft 1 25 perft 2 621 perft 3 16789 @@ -1406,7 +1406,7 @@ perft 5 13378840 perft 6 396575613 id 156 -epd nrnbqkbr/2pp2pp/4pp2/pp6/8/1P3P2/P1PPPBPP/NRNBQ1KR w hb - 0 9 +epd nrnbqkbr/2pp2pp/4pp2/pp6/8/1P3P2/P1PPPBPP/NRNBQ1KR w hb - perft 1 25 perft 2 656 perft 3 16951 @@ -1415,7 +1415,7 @@ perft 5 12525939 perft 6 358763789 id 157 -epd nrnqkbbr/ppppp1p1/7p/5p2/8/P4PP1/NPPPP2P/NR1QKBBR w HBhb - 0 9 +epd nrnqkbbr/ppppp1p1/7p/5p2/8/P4PP1/NPPPP2P/NR1QKBBR w HBhb - perft 1 28 perft 2 723 perft 3 20621 @@ -1424,7 +1424,7 @@ perft 5 15952533 perft 6 439046803 id 158 -epd 1rnqkr1b/ppppp2p/1n3pp1/8/2P3P1/Pb1N4/1P1PPP1P/NR1QKRBB w FBfb - 0 9 +epd 1rnqkr1b/ppppp2p/1n3pp1/8/2P3P1/Pb1N4/1P1PPP1P/NR1QKRBB w FBfb - perft 1 26 perft 2 713 perft 3 19671 @@ -1433,7 +1433,7 @@ perft 5 15865528 perft 6 454532806 id 159 -epd bbnrnkqr/1pppp1pp/5p2/p7/7P/1P6/PBPPPPPR/1BNRNKQ1 w D - 2 9 +epd bbnrnkqr/1pppp1pp/5p2/p7/7P/1P6/PBPPPPPR/1BNRNKQ1 w D - perft 1 26 perft 2 649 perft 3 17834 @@ -1442,7 +1442,7 @@ perft 5 14375839 perft 6 435585252 id 160 -epd bnrbk1qr/1ppp1ppp/p2np3/8/P7/2N2P2/1PPPP1PP/B1RBNKQR w HC - 0 9 +epd bnrbk1qr/1ppp1ppp/p2np3/8/P7/2N2P2/1PPPP1PP/B1RBNKQR w HC - perft 1 26 perft 2 621 perft 3 17569 @@ -1451,7 +1451,7 @@ perft 5 13514201 perft 6 364421088 id 161 -epd br1nkbqr/ppppppp1/8/n6p/8/N1P2PP1/PP1PP2P/B1RNKBQR w HCh - 1 9 +epd br1nkbqr/ppppppp1/8/n6p/8/N1P2PP1/PP1PP2P/B1RNKBQR w HCh - perft 1 29 perft 2 664 perft 3 20182 @@ -1460,7 +1460,7 @@ perft 5 16125924 perft 6 442508159 id 162 -epd bnr1kqrb/pp1pppp1/2n5/2p5/1P4Pp/4N3/P1PPPP1P/BNKR1QRB w gc - 0 9 +epd bnr1kqrb/pp1pppp1/2n5/2p5/1P4Pp/4N3/P1PPPP1P/BNKR1QRB w gc - perft 1 36 perft 2 888 perft 3 31630 @@ -1469,7 +1469,7 @@ perft 5 27792175 perft 6 719015345 id 163 -epd 1bbrnkqr/pp1p1ppp/2p1p3/1n6/5P2/3Q4/PPPPP1PP/NBBRNK1R w HDhd - 2 9 +epd 1bbrnkqr/pp1p1ppp/2p1p3/1n6/5P2/3Q4/PPPPP1PP/NBBRNK1R w HDhd - perft 1 36 perft 2 891 perft 3 31075 @@ -1478,7 +1478,7 @@ perft 5 26998966 perft 6 702903862 id 164 -epd nrbbnk1r/pp2pppq/8/2pp3p/3P2P1/1N6/PPP1PP1P/1RBBNKQR w HBhb - 0 9 +epd nrbbnk1r/pp2pppq/8/2pp3p/3P2P1/1N6/PPP1PP1P/1RBBNKQR w HBhb - perft 1 29 perft 2 1036 perft 3 31344 @@ -1487,7 +1487,7 @@ perft 5 35627310 perft 6 1310683359 id 165 -epd nr1nkbqr/ppp3pp/5p2/3pp3/6b1/3PP3/PPP2PPP/NRBNKBQR w hb - 0 9 +epd nr1nkbqr/ppp3pp/5p2/3pp3/6b1/3PP3/PPP2PPP/NRBNKBQR w hb - perft 1 18 perft 2 664 perft 3 13306 @@ -1496,7 +1496,7 @@ perft 5 10658989 perft 6 386307449 id 166 -epd nrbnk1rb/ppp1pq1p/3p4/5pp1/2P1P3/1N6/PP1PKPPP/1RBN1QRB w gb - 2 9 +epd nrbnk1rb/ppp1pq1p/3p4/5pp1/2P1P3/1N6/PP1PKPPP/1RBN1QRB w gb - perft 1 25 perft 2 966 perft 3 24026 @@ -1505,7 +1505,7 @@ perft 5 23957242 perft 6 913710194 id 167 -epd 1brnbkqr/pppppp2/6p1/7p/1Pn5/P1NP4/2P1PPPP/NBR1BKQR w HChc - 0 9 +epd 1brnbkqr/pppppp2/6p1/7p/1Pn5/P1NP4/2P1PPPP/NBR1BKQR w HChc - perft 1 22 perft 2 627 perft 3 13760 @@ -1514,7 +1514,7 @@ perft 5 9627826 perft 6 285900573 id 168 -epd nrnbbk1r/p1pppppq/8/7p/1p6/P5PP/1PPPPPQ1/NRNBBK1R w HBhb - 2 9 +epd nrnbbk1r/p1pppppq/8/7p/1p6/P5PP/1PPPPPQ1/NRNBBK1R w HBhb - perft 1 29 perft 2 888 perft 3 26742 @@ -1523,7 +1523,7 @@ perft 5 27229468 perft 6 930799376 id 169 -epd n1nkb1qr/prppppbp/6p1/1p6/2P2P2/P7/1P1PP1PP/NRNKBBQR w HBh - 1 9 +epd n1nkb1qr/prppppbp/6p1/1p6/2P2P2/P7/1P1PP1PP/NRNKBBQR w HBh - perft 1 29 perft 2 804 perft 3 24701 @@ -1532,7 +1532,7 @@ perft 5 21952444 perft 6 623156747 id 170 -epd nr2bqrb/ppkpp1pp/1np5/5p1P/5P2/2P5/PP1PP1P1/NRNKBQRB w GB - 0 9 +epd nr2bqrb/ppkpp1pp/1np5/5p1P/5P2/2P5/PP1PP1P1/NRNKBQRB w GB - perft 1 22 perft 2 530 perft 3 13055 @@ -1541,7 +1541,7 @@ perft 5 9244693 perft 6 264088392 id 171 -epd nbr1kqbr/p3pppp/2ppn3/1p4P1/4P3/1P6/P1PP1P1P/NBRNKQBR w HChc - 1 9 +epd nbr1kqbr/p3pppp/2ppn3/1p4P1/4P3/1P6/P1PP1P1P/NBRNKQBR w HChc - perft 1 23 perft 2 555 perft 3 14291 @@ -1550,7 +1550,7 @@ perft 5 9692630 perft 6 247479180 id 172 -epd nr1bkqbr/1p1pp1pp/pnp2p2/8/6P1/P1PP4/1P2PP1P/NRNBKQBR w HBhb - 0 9 +epd nr1bkqbr/1p1pp1pp/pnp2p2/8/6P1/P1PP4/1P2PP1P/NRNBKQBR w HBhb - perft 1 22 perft 2 565 perft 3 13343 @@ -1559,7 +1559,7 @@ perft 5 9305533 perft 6 268612479 id 173 -epd nr1kqbbr/np2pppp/p1p5/1B1p1P2/8/4P3/PPPP2PP/NRNKQ1BR w HBhb - 0 9 +epd nr1kqbbr/np2pppp/p1p5/1B1p1P2/8/4P3/PPPP2PP/NRNKQ1BR w HBhb - perft 1 32 perft 2 730 perft 3 23391 @@ -1568,7 +1568,7 @@ perft 5 18103280 perft 6 454569900 id 174 -epd nrnk1rbb/p1p2ppp/3pq3/Qp2p3/1P1P4/8/P1P1PPPP/NRN1KRBB w fb - 2 9 +epd nrnk1rbb/p1p2ppp/3pq3/Qp2p3/1P1P4/8/P1P1PPPP/NRN1KRBB w fb - perft 1 28 perft 2 873 perft 3 25683 @@ -1577,7 +1577,7 @@ perft 5 23868737 perft 6 747991356 id 175 -epd bbnrnkrq/pp1ppp1p/6p1/2p5/6P1/P5RP/1PPPPP2/BBNRNK1Q w Dgd - 3 9 +epd bbnrnkrq/pp1ppp1p/6p1/2p5/6P1/P5RP/1PPPPP2/BBNRNK1Q w Dgd - perft 1 37 perft 2 1260 perft 3 45060 @@ -1586,7 +1586,7 @@ perft 5 54843403 perft 6 1898432768 id 176 -epd bnrb1rkq/ppnpppp1/3Q4/2p4p/7P/N7/PPPPPPP1/B1RBNKR1 w GC - 2 9 +epd bnrb1rkq/ppnpppp1/3Q4/2p4p/7P/N7/PPPPPPP1/B1RBNKR1 w GC - perft 1 38 perft 2 878 perft 3 31944 @@ -1595,7 +1595,7 @@ perft 5 28784300 perft 6 784569826 id 177 -epd bnrnkbrq/p1ppppp1/1p5p/8/P2PP3/5P2/1PP3PP/BNRNKBRQ w GCgc - 1 9 +epd bnrnkbrq/p1ppppp1/1p5p/8/P2PP3/5P2/1PP3PP/BNRNKBRQ w GCgc - perft 1 26 perft 2 617 perft 3 16992 @@ -1604,7 +1604,7 @@ perft 5 11965544 perft 6 311309576 id 178 -epd bnrnkrqb/pp2p2p/2pp1pp1/8/P7/2PP1P2/1P2P1PP/BNRNKRQB w FCfc - 0 9 +epd bnrnkrqb/pp2p2p/2pp1pp1/8/P7/2PP1P2/1P2P1PP/BNRNKRQB w FCfc - perft 1 26 perft 2 721 perft 3 19726 @@ -1613,7 +1613,7 @@ perft 5 15966934 perft 6 467132503 id 179 -epd nbbrnkr1/1pppp1p1/p6q/P4p1p/8/5P2/1PPPP1PP/NBBRNRKQ w gd - 2 9 +epd nbbrnkr1/1pppp1p1/p6q/P4p1p/8/5P2/1PPPP1PP/NBBRNRKQ w gd - perft 1 18 perft 2 556 perft 3 10484 @@ -1622,7 +1622,7 @@ perft 5 6629293 perft 6 202528241 id 180 -epd nrb1nkrq/2pp1ppp/p4b2/1p2p3/P4B2/3P4/1PP1PPPP/NR1BNRKQ w gb - 0 9 +epd nrb1nkrq/2pp1ppp/p4b2/1p2p3/P4B2/3P4/1PP1PPPP/NR1BNRKQ w gb - perft 1 24 perft 2 562 perft 3 14017 @@ -1631,7 +1631,7 @@ perft 5 9227883 perft 6 247634489 id 181 -epd nrbnkbrq/p3p1pp/1p6/2pp1P2/8/3PP3/PPP2P1P/NRBNKBRQ w GBgb - 0 9 +epd nrbnkbrq/p3p1pp/1p6/2pp1P2/8/3PP3/PPP2P1P/NRBNKBRQ w GBgb - perft 1 31 perft 2 746 perft 3 24819 @@ -1640,7 +1640,7 @@ perft 5 21019301 perft 6 542954168 id 182 -epd nrbnkrqb/pppp1p1p/4p1p1/8/7P/2P1P3/PPNP1PP1/1RBNKRQB w FBfb - 0 9 +epd nrbnkrqb/pppp1p1p/4p1p1/8/7P/2P1P3/PPNP1PP1/1RBNKRQB w FBfb - perft 1 20 perft 2 459 perft 3 9998 @@ -1649,7 +1649,7 @@ perft 5 5760165 perft 6 146614723 id 183 -epd nbrn1krq/ppp1p2p/6b1/3p1pp1/8/4N1PP/PPPPPP2/NBR1BRKQ w gc - 1 9 +epd nbrn1krq/ppp1p2p/6b1/3p1pp1/8/4N1PP/PPPPPP2/NBR1BRKQ w gc - perft 1 27 perft 2 835 perft 3 23632 @@ -1658,7 +1658,7 @@ perft 5 22667987 perft 6 760795567 id 184 -epd nrnbbkrq/p1pp2pp/5p2/1p6/2P1pP1B/1P6/P2PP1PP/NRNB1KRQ w GBgb - 0 9 +epd nrnbbkrq/p1pp2pp/5p2/1p6/2P1pP1B/1P6/P2PP1PP/NRNB1KRQ w GBgb - perft 1 24 perft 2 646 perft 3 16102 @@ -1667,7 +1667,7 @@ perft 5 11489727 perft 6 324948755 id 185 -epd nrn1bbrq/1ppkppp1/p2p3p/8/1P3N2/4P3/P1PP1PPP/NR1KBBRQ w GB - 2 9 +epd nrn1bbrq/1ppkppp1/p2p3p/8/1P3N2/4P3/P1PP1PPP/NR1KBBRQ w GB - perft 1 32 perft 2 591 perft 3 18722 @@ -1676,7 +1676,7 @@ perft 5 12069159 perft 6 269922838 id 186 -epd n1krbrqb/1ppppppp/p7/8/4n3/P4P1P/1PPPPQP1/NRNKBR1B w FB - 2 9 +epd n1krbrqb/1ppppppp/p7/8/4n3/P4P1P/1PPPPQP1/NRNKBR1B w FB - perft 1 26 perft 2 639 perft 3 16988 @@ -1685,7 +1685,7 @@ perft 5 12167153 perft 6 312633873 id 187 -epd n1rnkrbq/1p1ppp1p/8/p1p1b1p1/3PQ1P1/4N3/PPP1PP1P/NBR1KRB1 w FCfc - 0 9 +epd n1rnkrbq/1p1ppp1p/8/p1p1b1p1/3PQ1P1/4N3/PPP1PP1P/NBR1KRB1 w FCfc - perft 1 35 perft 2 1027 perft 3 35731 @@ -1694,7 +1694,7 @@ perft 5 35738410 perft 6 1060661628 id 188 -epd nrnbkrbq/2pp1pp1/pp6/4p2p/P7/5PPP/1PPPP3/NRNBKRBQ w FBfb - 0 9 +epd nrnbkrbq/2pp1pp1/pp6/4p2p/P7/5PPP/1PPPP3/NRNBKRBQ w FBfb - perft 1 26 perft 2 628 perft 3 16731 @@ -1703,7 +1703,7 @@ perft 5 11920087 perft 6 331498921 id 189 -epd 1rnkrbbq/pp1p2pp/1n3p2/1Bp1p3/1P6/1N2P3/P1PP1PPP/1RNKR1BQ w EBeb - 0 9 +epd 1rnkrbbq/pp1p2pp/1n3p2/1Bp1p3/1P6/1N2P3/P1PP1PPP/1RNKR1BQ w EBeb - perft 1 33 perft 2 992 perft 3 32244 @@ -1712,7 +1712,7 @@ perft 5 31703749 perft 6 980306735 id 190 -epd nr1krqbb/p1ppppp1/8/1p5p/1Pn5/5P2/P1PPP1PP/NRNKRQBB w EBeb - 0 9 +epd nr1krqbb/p1ppppp1/8/1p5p/1Pn5/5P2/P1PPP1PP/NRNKRQBB w EBeb - perft 1 24 perft 2 670 perft 3 15985 @@ -1721,7 +1721,7 @@ perft 5 11371067 perft 6 325556465 id 191 -epd bbq1rkr1/1ppppppp/p1n2n2/8/2P2P2/1P6/PQ1PP1PP/BB1NRKNR w HEe - 3 9 +epd bbq1rkr1/1ppppppp/p1n2n2/8/2P2P2/1P6/PQ1PP1PP/BB1NRKNR w HEe - perft 1 32 perft 2 794 perft 3 26846 @@ -1730,7 +1730,7 @@ perft 5 24085223 perft 6 645633370 id 192 -epd b1nbrknr/1qppp1pp/p4p2/1p6/6P1/P2NP3/1PPP1P1P/BQ1BRKNR w HEhe - 1 9 +epd b1nbrknr/1qppp1pp/p4p2/1p6/6P1/P2NP3/1PPP1P1P/BQ1BRKNR w HEhe - perft 1 25 perft 2 663 perft 3 17138 @@ -1739,7 +1739,7 @@ perft 5 13157826 perft 6 389603029 id 193 -epd bqnrk1nr/pp2ppbp/6p1/2pp4/2P5/5P2/PPQPP1PP/B1NRKBNR w HDhd - 0 9 +epd bqnrk1nr/pp2ppbp/6p1/2pp4/2P5/5P2/PPQPP1PP/B1NRKBNR w HDhd - perft 1 26 perft 2 850 perft 3 22876 @@ -1748,7 +1748,7 @@ perft 5 21341087 perft 6 719712622 id 194 -epd bqnrknrb/1ppp1p1p/p7/6p1/1P2p3/P1PN4/3PPPPP/BQ1RKNRB w GDgd - 0 9 +epd bqnrknrb/1ppp1p1p/p7/6p1/1P2p3/P1PN4/3PPPPP/BQ1RKNRB w GDgd - perft 1 25 perft 2 721 perft 3 19290 @@ -1757,7 +1757,7 @@ perft 5 16391601 perft 6 511725087 id 195 -epd q1b1rknr/pp1pppp1/4n2p/2p1b3/1PP5/4P3/PQ1P1PPP/1BBNRKNR w HEhe - 1 9 +epd q1b1rknr/pp1pppp1/4n2p/2p1b3/1PP5/4P3/PQ1P1PPP/1BBNRKNR w HEhe - perft 1 32 perft 2 975 perft 3 32566 @@ -1766,7 +1766,7 @@ perft 5 32649943 perft 6 962536105 id 196 -epd qnbbrknr/1p1ppppp/8/p1p5/5P2/PP1P4/2P1P1PP/QNBBRKNR w HEhe - 0 9 +epd qnbbrknr/1p1ppppp/8/p1p5/5P2/PP1P4/2P1P1PP/QNBBRKNR w HEhe - perft 1 27 perft 2 573 perft 3 16331 @@ -1775,7 +1775,7 @@ perft 5 11562434 perft 6 301166330 id 197 -epd q1brkb1r/p1pppppp/np3B2/8/6n1/1P5N/P1PPPPPP/QN1RKB1R w HDhd - 0 9 +epd q1brkb1r/p1pppppp/np3B2/8/6n1/1P5N/P1PPPPPP/QN1RKB1R w HDhd - perft 1 32 perft 2 984 perft 3 31549 @@ -1784,7 +1784,7 @@ perft 5 32597704 perft 6 1075429389 id 198 -epd qn1rk1rb/p1pppppp/1p2n3/8/2b5/4NPP1/PPPPP1RP/QNBRK2B w Dgd - 4 9 +epd qn1rk1rb/p1pppppp/1p2n3/8/2b5/4NPP1/PPPPP1RP/QNBRK2B w Dgd - perft 1 22 perft 2 802 perft 3 19156 @@ -1793,7 +1793,7 @@ perft 5 17761431 perft 6 650603534 id 199 -epd qbnrbknr/ppp2p1p/8/3pp1p1/1PP1B3/5N2/P2PPPPP/Q1NRBK1R w HDhd - 0 9 +epd qbnrbknr/ppp2p1p/8/3pp1p1/1PP1B3/5N2/P2PPPPP/Q1NRBK1R w HDhd - perft 1 34 perft 2 943 perft 3 32506 @@ -1802,7 +1802,7 @@ perft 5 32523099 perft 6 955802240 id 200 -epd qnrbb1nr/pp1p1ppp/2p2k2/4p3/4P3/5PPP/PPPP4/QNRBBKNR w HC - 0 9 +epd qnrbb1nr/pp1p1ppp/2p2k2/4p3/4P3/5PPP/PPPP4/QNRBBKNR w HC - perft 1 20 perft 2 460 perft 3 10287 @@ -1811,7 +1811,7 @@ perft 5 5846781 perft 6 140714047 id 201 -epd qnr1bbnr/ppk1p1pp/3p4/2p2p2/8/2P5/PP1PPPPP/QNKRBBNR w - - 1 9 +epd qnr1bbnr/ppk1p1pp/3p4/2p2p2/8/2P5/PP1PPPPP/QNKRBBNR w - - perft 1 19 perft 2 572 perft 3 11834 @@ -1820,7 +1820,7 @@ perft 5 7994547 perft 6 243724815 id 202 -epd qnrkbnrb/1p1p1ppp/2p5/4p3/p7/N1BP4/PPP1PPPP/Q1R1KNRB w gc - 0 9 +epd qnrkbnrb/1p1p1ppp/2p5/4p3/p7/N1BP4/PPP1PPPP/Q1R1KNRB w gc - perft 1 27 perft 2 579 perft 3 16233 @@ -1829,7 +1829,7 @@ perft 5 10845146 perft 6 268229097 id 203 -epd qbnrkn1r/1pppp1p1/p3bp2/2BN3p/8/5P2/PPPPP1PP/QBNRK2R w HDhd - 0 9 +epd qbnrkn1r/1pppp1p1/p3bp2/2BN3p/8/5P2/PPPPP1PP/QBNRK2R w HDhd - perft 1 40 perft 2 1027 perft 3 38728 @@ -1838,7 +1838,7 @@ perft 5 38511307 perft 6 1104094381 id 204 -epd qnrbknbr/1pp2ppp/4p3/p6N/2p5/8/PPPPPPPP/Q1RBK1BR w HChc - 0 9 +epd qnrbknbr/1pp2ppp/4p3/p6N/2p5/8/PPPPPPPP/Q1RBK1BR w HChc - perft 1 22 perft 2 510 perft 3 11844 @@ -1847,7 +1847,7 @@ perft 5 7403327 perft 6 200581103 id 205 -epd 1qkrnbbr/p1pppppp/2n5/1p6/8/5NP1/PPPPPP1P/QNRK1BBR w HC - 4 9 +epd 1qkrnbbr/p1pppppp/2n5/1p6/8/5NP1/PPPPPP1P/QNRK1BBR w HC - perft 1 24 perft 2 549 perft 3 13987 @@ -1856,7 +1856,7 @@ perft 5 9396521 perft 6 255676649 id 206 -epd q1rknr1b/1ppppppb/2n5/p2B3p/8/1PN3P1/P1PPPP1P/Q1RKNRB1 w FCfc - 3 9 +epd q1rknr1b/1ppppppb/2n5/p2B3p/8/1PN3P1/P1PPPP1P/Q1RKNRB1 w FCfc - perft 1 31 perft 2 924 perft 3 28520 @@ -1865,7 +1865,7 @@ perft 5 27463479 perft 6 847726572 id 207 -epd bbnqrk1r/pp1pppp1/2p4p/8/6n1/1N1P1P2/PPP1P1PP/BBQ1RKNR w HEhe - 4 9 +epd bbnqrk1r/pp1pppp1/2p4p/8/6n1/1N1P1P2/PPP1P1PP/BBQ1RKNR w HEhe - perft 1 24 perft 2 804 perft 3 20147 @@ -1874,7 +1874,7 @@ perft 5 18024195 perft 6 595947631 id 208 -epd bn1brknr/ppp1p1pp/5p2/3p4/6qQ/3P3P/PPP1PPP1/BN1BRKNR w HEhe - 4 9 +epd bn1brknr/ppp1p1pp/5p2/3p4/6qQ/3P3P/PPP1PPP1/BN1BRKNR w HEhe - perft 1 25 perft 2 854 perft 3 22991 @@ -1883,7 +1883,7 @@ perft 5 20290974 perft 6 600195008 id 209 -epd 1nqrkbnr/2pp1ppp/pp2p3/3b4/2P5/N7/PP1PPPPP/B1QRKBNR w HDhd - 0 9 +epd 1nqrkbnr/2pp1ppp/pp2p3/3b4/2P5/N7/PP1PPPPP/B1QRKBNR w HDhd - perft 1 22 perft 2 651 perft 3 16173 @@ -1892,7 +1892,7 @@ perft 5 13133439 perft 6 390886040 id 210 -epd bnqrk1rb/1pp1pppp/p2p4/4n3/2PPP3/8/PP3PPP/BNQRKNRB w GDgd - 1 9 +epd bnqrk1rb/1pp1pppp/p2p4/4n3/2PPP3/8/PP3PPP/BNQRKNRB w GDgd - perft 1 30 perft 2 950 perft 3 28169 @@ -1901,7 +1901,7 @@ perft 5 27610213 perft 6 880739164 id 211 -epd nbb1rknr/1ppq1ppp/3p4/p3p3/4P3/1N2R3/PPPP1PPP/1BBQ1KNR w Hhe - 2 9 +epd nbb1rknr/1ppq1ppp/3p4/p3p3/4P3/1N2R3/PPPP1PPP/1BBQ1KNR w Hhe - perft 1 33 perft 2 988 perft 3 31293 @@ -1910,7 +1910,7 @@ perft 5 30894863 perft 6 985384035 id 212 -epd nqbbrknr/2ppp2p/pp4p1/5p2/7P/3P1P2/PPPBP1P1/NQ1BRKNR w HEhe - 0 9 +epd nqbbrknr/2ppp2p/pp4p1/5p2/7P/3P1P2/PPPBP1P1/NQ1BRKNR w HEhe - perft 1 27 perft 2 492 perft 3 13266 @@ -1919,7 +1919,7 @@ perft 5 7583292 perft 6 175376176 id 213 -epd 1qbrkb1r/pppppppp/8/3n4/4P1n1/PN6/1PPP1P1P/1QBRKBNR w HDhd - 3 9 +epd 1qbrkb1r/pppppppp/8/3n4/4P1n1/PN6/1PPP1P1P/1QBRKBNR w HDhd - perft 1 28 perft 2 800 perft 3 21982 @@ -1928,7 +1928,7 @@ perft 5 17313279 perft 6 507140861 id 214 -epd 1qbrknrb/1p1ppppp/1np5/8/p4P1P/4P1N1/PPPP2P1/NQBRK1RB w GDgd - 0 9 +epd 1qbrknrb/1p1ppppp/1np5/8/p4P1P/4P1N1/PPPP2P1/NQBRK1RB w GDgd - perft 1 21 perft 2 482 perft 3 10581 @@ -1937,7 +1937,7 @@ perft 5 6218644 perft 6 168704845 id 215 -epd nbqrbkr1/ppp1pppp/8/3p4/6n1/2P2PPN/PP1PP2P/NBQRBK1R w HDd - 1 9 +epd nbqrbkr1/ppp1pppp/8/3p4/6n1/2P2PPN/PP1PP2P/NBQRBK1R w HDd - perft 1 29 perft 2 921 perft 3 25748 @@ -1946,7 +1946,7 @@ perft 5 24138518 perft 6 806554650 id 216 -epd nqrb1knr/1ppbpp1p/p7/3p2p1/2P3P1/5P1P/PP1PP3/NQRBBKNR w HChc - 1 9 +epd nqrb1knr/1ppbpp1p/p7/3p2p1/2P3P1/5P1P/PP1PP3/NQRBBKNR w HChc - perft 1 31 perft 2 803 perft 3 25857 @@ -1955,7 +1955,7 @@ perft 5 21998733 perft 6 583349773 id 217 -epd 1qrkbbr1/pppp1ppp/1n3n2/4p3/5P2/1N6/PPPPP1PP/1QRKBBNR w HCc - 0 9 +epd 1qrkbbr1/pppp1ppp/1n3n2/4p3/5P2/1N6/PPPPP1PP/1QRKBBNR w HCc - perft 1 25 perft 2 715 perft 3 19118 @@ -1964,7 +1964,7 @@ perft 5 15514933 perft 6 459533767 id 218 -epd nqrkb1rb/pp2pppp/2p1n3/3p4/3PP1N1/8/PPP2PPP/NQRKB1RB w GCgc - 0 9 +epd nqrkb1rb/pp2pppp/2p1n3/3p4/3PP1N1/8/PPP2PPP/NQRKB1RB w GCgc - perft 1 26 perft 2 795 perft 3 21752 @@ -1973,7 +1973,7 @@ perft 5 19185851 perft 6 616508881 id 219 -epd nb1rknbr/pp2ppp1/8/2Bp3p/6P1/2P2P1q/PP1PP2P/NBQRKN1R w HDhd - 0 9 +epd nb1rknbr/pp2ppp1/8/2Bp3p/6P1/2P2P1q/PP1PP2P/NBQRKN1R w HDhd - perft 1 35 perft 2 1391 perft 3 43025 @@ -1982,7 +1982,7 @@ perft 5 53033675 perft 6 2139267832 id 220 -epd nqrbkn1r/pp1pp1pp/8/2p2p2/5P2/P3B2P/1PbPP1P1/NQRBKN1R w HChc - 0 9 +epd nqrbkn1r/pp1pp1pp/8/2p2p2/5P2/P3B2P/1PbPP1P1/NQRBKN1R w HChc - perft 1 23 perft 2 758 perft 3 19439 @@ -1991,7 +1991,7 @@ perft 5 18296195 perft 6 628403401 id 221 -epd nqrknbbr/pp1pppp1/7p/2p5/7P/1P1N4/P1PPPPPB/NQRK1B1R w HChc - 2 9 +epd nqrknbbr/pp1pppp1/7p/2p5/7P/1P1N4/P1PPPPPB/NQRK1B1R w HChc - perft 1 29 perft 2 824 perft 3 23137 @@ -2000,7 +2000,7 @@ perft 5 19429491 perft 6 595493802 id 222 -epd 1qrknrbb/B1p1pppp/8/1p1p4/2n2P2/1P6/P1PPP1PP/NQRKNR1B w FCfc - 0 9 +epd 1qrknrbb/B1p1pppp/8/1p1p4/2n2P2/1P6/P1PPP1PP/NQRKNR1B w FCfc - perft 1 28 perft 2 771 perft 3 20237 @@ -2009,7 +2009,7 @@ perft 5 16065378 perft 6 483037840 id 223 -epd bbnrqk1r/1ppppppp/8/7n/1p6/P6P/1BPPPPP1/1BNRQKNR w HDhd - 0 9 +epd bbnrqk1r/1ppppppp/8/7n/1p6/P6P/1BPPPPP1/1BNRQKNR w HDhd - perft 1 25 perft 2 601 perft 3 15471 @@ -2018,7 +2018,7 @@ perft 5 10697065 perft 6 289472497 id 224 -epd bnrbqknr/ppp3p1/3ppp1Q/7p/3P4/1P6/P1P1PPPP/BNRB1KNR w HChc - 0 9 +epd bnrbqknr/ppp3p1/3ppp1Q/7p/3P4/1P6/P1P1PPPP/BNRB1KNR w HChc - perft 1 32 perft 2 845 perft 3 26876 @@ -2027,7 +2027,7 @@ perft 5 23717883 perft 6 682154649 id 225 -epd bn1qkb1r/pprppppp/8/2p5/2PPP1n1/8/PPR2PPP/BN1QKBNR w Hh - 1 9 +epd bn1qkb1r/pprppppp/8/2p5/2PPP1n1/8/PPR2PPP/BN1QKBNR w Hh - perft 1 32 perft 2 856 perft 3 27829 @@ -2036,7 +2036,7 @@ perft 5 25245957 perft 6 727424329 id 226 -epd 1nrqknrb/p1pp1ppp/1p2p3/3N4/5P1P/5b2/PPPPP3/B1RQKNRB w GCgc - 2 9 +epd 1nrqknrb/p1pp1ppp/1p2p3/3N4/5P1P/5b2/PPPPP3/B1RQKNRB w GCgc - perft 1 33 perft 2 873 perft 3 27685 @@ -2045,7 +2045,7 @@ perft 5 25128076 perft 6 745401024 id 227 -epd nbbrqrk1/pppppppp/8/2N1n3/P7/6P1/1PPPPP1P/1BBRQKNR w HD - 3 9 +epd nbbrqrk1/pppppppp/8/2N1n3/P7/6P1/1PPPPP1P/1BBRQKNR w HD - perft 1 25 perft 2 555 perft 3 14339 @@ -2054,7 +2054,7 @@ perft 5 9153089 perft 6 234841945 id 228 -epd 1rbbqknr/1ppp1pp1/1n2p3/p6p/4P1P1/P6N/1PPP1P1P/NRBBQK1R w HBhb - 0 9 +epd 1rbbqknr/1ppp1pp1/1n2p3/p6p/4P1P1/P6N/1PPP1P1P/NRBBQK1R w HBhb - perft 1 25 perft 2 693 perft 3 18652 @@ -2063,7 +2063,7 @@ perft 5 15133381 perft 6 439344945 id 229 -epd nrq1kbnr/p1pbpppp/3p4/1p6/6P1/1N3N2/PPPPPP1P/1RBQKB1R w HBhb - 4 9 +epd nrq1kbnr/p1pbpppp/3p4/1p6/6P1/1N3N2/PPPPPP1P/1RBQKB1R w HBhb - perft 1 24 perft 2 648 perft 3 16640 @@ -2072,7 +2072,7 @@ perft 5 12871967 perft 6 380436777 id 230 -epd nr1qknr1/p1pppp1p/b5p1/1p6/8/P4PP1/1bPPP1RP/NRBQKN1B w Bgb - 0 9 +epd nr1qknr1/p1pppp1p/b5p1/1p6/8/P4PP1/1bPPP1RP/NRBQKN1B w Bgb - perft 1 18 perft 2 533 perft 3 11215 @@ -2081,7 +2081,7 @@ perft 5 7777833 perft 6 234905172 id 231 -epd nbrqbknr/1ppp2pp/8/4pp2/p2PP1P1/7N/PPP2P1P/NBRQBK1R w HChc - 0 9 +epd nbrqbknr/1ppp2pp/8/4pp2/p2PP1P1/7N/PPP2P1P/NBRQBK1R w HChc - perft 1 29 perft 2 803 perft 3 24416 @@ -2090,7 +2090,7 @@ perft 5 22305910 perft 6 672322762 id 232 -epd nr1b1k1r/ppp1pppp/2bp1n2/6P1/2P3q1/5P2/PP1PP2P/NRQBBKNR w HBhb - 1 9 +epd nr1b1k1r/ppp1pppp/2bp1n2/6P1/2P3q1/5P2/PP1PP2P/NRQBBKNR w HBhb - perft 1 27 perft 2 1199 perft 3 30908 @@ -2099,7 +2099,7 @@ perft 5 35121759 perft 6 1418677099 id 233 -epd nrqkbbnr/2pppp1p/p7/1p6/2P1Pp2/8/PPNP2PP/1RQKBBNR w HBhb - 0 9 +epd nrqkbbnr/2pppp1p/p7/1p6/2P1Pp2/8/PPNP2PP/1RQKBBNR w HBhb - perft 1 28 perft 2 613 perft 3 17874 @@ -2108,7 +2108,7 @@ perft 5 13097064 perft 6 345294379 id 234 -epd 1rqkbnrb/pp1ppp1p/1n4p1/B1p5/3PP3/4N3/PPP2PPP/NRQK2RB w GBgb - 0 9 +epd 1rqkbnrb/pp1ppp1p/1n4p1/B1p5/3PP3/4N3/PPP2PPP/NRQK2RB w GBgb - perft 1 33 perft 2 723 perft 3 23991 @@ -2117,7 +2117,7 @@ perft 5 19715083 perft 6 535650233 id 235 -epd nbrqkn1r/1pppp2p/5pp1/p2b4/5P2/P2PN3/1PP1P1PP/NBRQK1BR w HChc - 2 9 +epd nbrqkn1r/1pppp2p/5pp1/p2b4/5P2/P2PN3/1PP1P1PP/NBRQK1BR w HChc - perft 1 23 perft 2 607 perft 3 15482 @@ -2126,7 +2126,7 @@ perft 5 11026383 perft 6 290708878 id 236 -epd nrqbknbr/pp1pppp1/8/2p4p/P3PP2/8/1PPP2PP/NRQBKNBR w HBhb - 1 9 +epd nrqbknbr/pp1pppp1/8/2p4p/P3PP2/8/1PPP2PP/NRQBKNBR w HBhb - perft 1 26 perft 2 700 perft 3 19371 @@ -2135,7 +2135,7 @@ perft 5 16058815 perft 6 485460242 id 237 -epd nrqknbbr/p2pppp1/1pp5/6Qp/3P4/1P3P2/P1P1P1PP/NR1KNBBR w HBhb - 0 9 +epd nrqknbbr/p2pppp1/1pp5/6Qp/3P4/1P3P2/P1P1P1PP/NR1KNBBR w HBhb - perft 1 40 perft 2 905 perft 3 32932 @@ -2144,7 +2144,7 @@ perft 5 29263502 perft 6 791963709 id 238 -epd nrqknrbb/1p3ppp/p2p4/2p1p3/1P6/3PP1P1/P1P2P1P/NRQKNRBB w FBfb - 0 9 +epd nrqknrbb/1p3ppp/p2p4/2p1p3/1P6/3PP1P1/P1P2P1P/NRQKNRBB w FBfb - perft 1 29 perft 2 780 perft 3 22643 @@ -2153,7 +2153,7 @@ perft 5 19532077 perft 6 593181101 id 239 -epd 1bnrkqnr/p1pppp2/7p/1p4p1/4b3/7N/PPPP1PPP/BBNRKQ1R w HDhd - 0 9 +epd 1bnrkqnr/p1pppp2/7p/1p4p1/4b3/7N/PPPP1PPP/BBNRKQ1R w HDhd - perft 1 25 perft 2 725 perft 3 19808 @@ -2162,7 +2162,7 @@ perft 5 16661676 perft 6 487354613 id 240 -epd bnrbkq1r/pp2p1pp/5n2/2pp1p2/P7/N1PP4/1P2PPPP/B1RBKQNR w HChc - 1 9 +epd bnrbkq1r/pp2p1pp/5n2/2pp1p2/P7/N1PP4/1P2PPPP/B1RBKQNR w HChc - perft 1 24 perft 2 745 perft 3 18494 @@ -2171,7 +2171,7 @@ perft 5 15079602 perft 6 488924040 id 241 -epd 2rkqbnr/p1pppppp/2b5/1pn5/1P3P1Q/2B5/P1PPP1PP/1NRK1BNR w HChc - 3 9 +epd 2rkqbnr/p1pppppp/2b5/1pn5/1P3P1Q/2B5/P1PPP1PP/1NRK1BNR w HChc - perft 1 33 perft 2 904 perft 3 30111 @@ -2180,7 +2180,7 @@ perft 5 28194726 perft 6 801757709 id 242 -epd bnrkqnrb/2pppp2/8/pp4pp/1P5P/6P1/P1PPPPB1/BNRKQNR1 w GCgc - 0 9 +epd bnrkqnrb/2pppp2/8/pp4pp/1P5P/6P1/P1PPPPB1/BNRKQNR1 w GCgc - perft 1 34 perft 2 1059 perft 3 34090 @@ -2189,7 +2189,7 @@ perft 5 33195397 perft 6 1036498304 id 243 -epd 1bbrkq1r/pppp2pp/1n2pp1n/8/2PP4/1N4P1/PP2PP1P/1BBRKQNR w HDhd - 1 9 +epd 1bbrkq1r/pppp2pp/1n2pp1n/8/2PP4/1N4P1/PP2PP1P/1BBRKQNR w HDhd - perft 1 33 perft 2 891 perft 3 28907 @@ -2198,7 +2198,7 @@ perft 5 26970098 perft 6 788040469 id 244 -epd nrbbkqnr/1p2pp1p/p1p3p1/3p4/8/1PP5/P2PPPPP/NRBBKQNR w HBhb - 0 9 +epd nrbbkqnr/1p2pp1p/p1p3p1/3p4/8/1PP5/P2PPPPP/NRBBKQNR w HBhb - perft 1 21 perft 2 567 perft 3 13212 @@ -2207,7 +2207,7 @@ perft 5 9539687 perft 6 284426039 id 245 -epd 1rbkqbr1/ppp1pppp/1n5n/3p4/3P4/1PP3P1/P3PP1P/NRBKQBNR w HBb - 1 9 +epd 1rbkqbr1/ppp1pppp/1n5n/3p4/3P4/1PP3P1/P3PP1P/NRBKQBNR w HBb - perft 1 27 perft 2 752 perft 3 20686 @@ -2216,7 +2216,7 @@ perft 5 16986290 perft 6 521817800 id 246 -epd nrbkq1rb/1ppp1pp1/4p1n1/p6p/2PP4/5P2/PPK1P1PP/NRB1QNRB w gb - 0 9 +epd nrbkq1rb/1ppp1pp1/4p1n1/p6p/2PP4/5P2/PPK1P1PP/NRB1QNRB w gb - perft 1 35 perft 2 697 perft 3 23678 @@ -2225,7 +2225,7 @@ perft 5 16906409 perft 6 390324794 id 247 -epd nbrkbqnr/p2pp1p1/5p2/1pp4p/7P/3P2P1/PPP1PP2/NBKRBQNR w hc - 0 9 +epd nbrkbqnr/p2pp1p1/5p2/1pp4p/7P/3P2P1/PPP1PP2/NBKRBQNR w hc - perft 1 25 perft 2 679 perft 3 17223 @@ -2234,7 +2234,7 @@ perft 5 12879258 perft 6 376652259 id 248 -epd nrkb1qnr/ppppp1p1/6bp/5p2/1PP1P1P1/8/P2P1P1P/NRKBBQNR w HBhb - 1 9 +epd nrkb1qnr/ppppp1p1/6bp/5p2/1PP1P1P1/8/P2P1P1P/NRKBBQNR w HBhb - perft 1 32 perft 2 761 perft 3 24586 @@ -2243,7 +2243,7 @@ perft 5 20671433 perft 6 568524724 id 249 -epd nrk1bbnr/p1q1pppp/1ppp4/8/3P3P/4K3/PPP1PPP1/NR1QBBNR w hb - 0 9 +epd nrk1bbnr/p1q1pppp/1ppp4/8/3P3P/4K3/PPP1PPP1/NR1QBBNR w hb - perft 1 30 perft 2 719 perft 3 21683 @@ -2252,7 +2252,7 @@ perft 5 16278120 perft 6 423649784 id 250 -epd nrkqbr1b/1pppp1pp/5pn1/p6N/1P3P2/8/P1PPP1PP/NRKQB1RB w GBb - 0 9 +epd nrkqbr1b/1pppp1pp/5pn1/p6N/1P3P2/8/P1PPP1PP/NRKQB1RB w GBb - perft 1 26 perft 2 494 perft 3 13815 @@ -2261,7 +2261,7 @@ perft 5 8763742 perft 6 206993496 id 251 -epd nbrkq2r/pppp1bpp/4p1n1/5p2/7P/2P3N1/PP1PPPP1/NBKRQ1BR w hc - 0 9 +epd nbrkq2r/pppp1bpp/4p1n1/5p2/7P/2P3N1/PP1PPPP1/NBKRQ1BR w hc - perft 1 27 perft 2 701 perft 3 19536 @@ -2270,7 +2270,7 @@ perft 5 15394667 perft 6 443506342 id 252 -epd nrkbqnbr/2ppp2p/pp6/5pp1/P1P5/8/1P1PPPPP/NRKBQNBR w HBhb - 0 9 +epd nrkbqnbr/2ppp2p/pp6/5pp1/P1P5/8/1P1PPPPP/NRKBQNBR w HBhb - perft 1 21 perft 2 487 perft 3 11341 @@ -2279,7 +2279,7 @@ perft 5 7218486 perft 6 193586674 id 253 -epd nr1qnbbr/pk1pppp1/1pp4p/8/3P4/5P1P/PPP1P1P1/NRKQNBBR w HB - 0 9 +epd nr1qnbbr/pk1pppp1/1pp4p/8/3P4/5P1P/PPP1P1P1/NRKQNBBR w HB - perft 1 22 perft 2 546 perft 3 13615 @@ -2288,7 +2288,7 @@ perft 5 9587439 perft 6 259830255 id 254 -epd nrkq1rbb/pp1ppp1p/2pn4/8/PP3Pp1/7P/2PPP1P1/NRKQNRBB w FBfb - 0 9 +epd nrkq1rbb/pp1ppp1p/2pn4/8/PP3Pp1/7P/2PPP1P1/NRKQNRBB w FBfb - perft 1 26 perft 2 839 perft 3 22075 @@ -2297,7 +2297,7 @@ perft 5 19867117 perft 6 658535326 id 255 -epd b2rknqr/pp1ppppp/8/2P5/n7/P7/1PPNPPPb/BBNRK1QR w HDhd - 2 9 +epd b2rknqr/pp1ppppp/8/2P5/n7/P7/1PPNPPPb/BBNRK1QR w HDhd - perft 1 24 perft 2 699 perft 3 19523 @@ -2306,7 +2306,7 @@ perft 5 17734818 perft 6 535094237 id 256 -epd bnrbknqr/pp2p2p/2p3p1/3p1p2/8/3P4/PPPNPPPP/B1RBKNQR w HChc - 0 9 +epd bnrbknqr/pp2p2p/2p3p1/3p1p2/8/3P4/PPPNPPPP/B1RBKNQR w HChc - perft 1 23 perft 2 580 perft 3 14320 @@ -2315,7 +2315,7 @@ perft 5 10133092 perft 6 288041554 id 257 -epd bnrknb1r/pppp2pp/8/4pp2/6P1/3P3P/qPP1PPQ1/BNRKNB1R w HChc - 0 9 +epd bnrknb1r/pppp2pp/8/4pp2/6P1/3P3P/qPP1PPQ1/BNRKNB1R w HChc - perft 1 28 perft 2 1100 perft 3 31813 @@ -2324,7 +2324,7 @@ perft 5 36142423 perft 6 1361341249 id 258 -epd b1rknqrb/ppp1p1p1/2np1p1p/8/4N3/6PQ/PPPPPP1P/B1RKN1RB w GCgc - 0 9 +epd b1rknqrb/ppp1p1p1/2np1p1p/8/4N3/6PQ/PPPPPP1P/B1RKN1RB w GCgc - perft 1 36 perft 2 629 perft 3 23082 @@ -2333,7 +2333,7 @@ perft 5 16897544 perft 6 367503974 id 259 -epd nb1rknqr/pbppp2p/6p1/1p3p2/5P2/3KP3/PPPP2PP/NBBR1NQR w hd - 2 9 +epd nb1rknqr/pbppp2p/6p1/1p3p2/5P2/3KP3/PPPP2PP/NBBR1NQR w hd - perft 1 18 perft 2 557 perft 3 9779 @@ -2342,7 +2342,7 @@ perft 5 5822387 perft 6 180936551 id 260 -epd nr1bknqr/1ppb1ppp/p7/3pp3/B7/2P3NP/PP1PPPP1/NRB1K1QR w HBhb - 2 9 +epd nr1bknqr/1ppb1ppp/p7/3pp3/B7/2P3NP/PP1PPPP1/NRB1K1QR w HBhb - perft 1 28 perft 2 688 perft 3 19541 @@ -2351,7 +2351,7 @@ perft 5 15153092 perft 6 425149249 id 261 -epd nrbkn2r/pppp1pqp/4p1p1/8/3P2P1/P3B3/P1P1PP1P/NR1KNBQR w HBhb - 1 9 +epd nrbkn2r/pppp1pqp/4p1p1/8/3P2P1/P3B3/P1P1PP1P/NR1KNBQR w HBhb - perft 1 32 perft 2 808 perft 3 25578 @@ -2360,7 +2360,7 @@ perft 5 22094260 perft 6 609377239 id 262 -epd nrbknqrb/2p1ppp1/1p6/p2p2Bp/1P6/3P1P2/P1P1P1PP/NR1KNQRB w GBgb - 0 9 +epd nrbknqrb/2p1ppp1/1p6/p2p2Bp/1P6/3P1P2/P1P1P1PP/NR1KNQRB w GBgb - perft 1 30 perft 2 625 perft 3 18288 @@ -2369,7 +2369,7 @@ perft 5 12225742 perft 6 301834282 id 263 -epd nbr1knqr/1pp1p1pp/3p1pb1/8/7P/5P2/PPPPPQP1/NBRKBN1R w HC - 2 9 +epd nbr1knqr/1pp1p1pp/3p1pb1/8/7P/5P2/PPPPPQP1/NBRKBN1R w HC - perft 1 29 perft 2 863 perft 3 25767 @@ -2378,7 +2378,7 @@ perft 5 24965592 perft 6 799182442 id 264 -epd n1kbbnqr/prp2ppp/1p1p4/4p3/1P2P3/3P1B2/P1P2PPP/NRK1BNQR w HBh - 2 9 +epd n1kbbnqr/prp2ppp/1p1p4/4p3/1P2P3/3P1B2/P1P2PPP/NRK1BNQR w HBh - perft 1 26 perft 2 653 perft 3 17020 @@ -2387,7 +2387,7 @@ perft 5 12187583 perft 6 336872952 id 265 -epd nrknbbqr/pp3p1p/B3p1p1/2pp4/4P3/2N3P1/PPPP1P1P/NRK1B1QR w HBhb - 0 9 +epd nrknbbqr/pp3p1p/B3p1p1/2pp4/4P3/2N3P1/PPPP1P1P/NRK1B1QR w HBhb - perft 1 29 perft 2 683 perft 3 19755 @@ -2396,7 +2396,7 @@ perft 5 14684565 perft 6 394951291 id 266 -epd n1knbqrb/pr1p1ppp/Qp6/2p1p3/4P3/6P1/PPPP1P1P/NRKNB1RB w GBg - 2 9 +epd n1knbqrb/pr1p1ppp/Qp6/2p1p3/4P3/6P1/PPPP1P1P/NRKNB1RB w GBg - perft 1 31 perft 2 552 perft 3 17197 @@ -2405,7 +2405,7 @@ perft 5 11663330 perft 6 283583340 id 267 -epd nbrknqbr/p3p1pp/1p1p1p2/2p5/2Q1PP2/8/PPPP2PP/NBRKN1BR w HChc - 0 9 +epd nbrknqbr/p3p1pp/1p1p1p2/2p5/2Q1PP2/8/PPPP2PP/NBRKN1BR w HChc - perft 1 37 perft 2 913 perft 3 32470 @@ -2414,7 +2414,7 @@ perft 5 28899548 perft 6 759875563 id 268 -epd nrkb1qbr/pp1pppp1/5n2/7p/2p5/1N1NPP2/PPPP2PP/1RKB1QBR w HBhb - 0 9 +epd nrkb1qbr/pp1pppp1/5n2/7p/2p5/1N1NPP2/PPPP2PP/1RKB1QBR w HBhb - perft 1 25 perft 2 712 perft 3 18813 @@ -2423,7 +2423,7 @@ perft 5 15045589 perft 6 445074372 id 269 -epd nrk2bbr/pppqpppp/3p4/8/1P3nP1/3P4/P1P1PP1P/NRKNQBBR w HBhb - 1 9 +epd nrk2bbr/pppqpppp/3p4/8/1P3nP1/3P4/P1P1PP1P/NRKNQBBR w HBhb - perft 1 24 perft 2 814 perft 3 19954 @@ -2432,7 +2432,7 @@ perft 5 17603960 perft 6 592121050 id 270 -epd nrknqrbb/1p2ppp1/2pp4/Q6p/P2P3P/8/1PP1PPP1/NRKN1RBB w FBfb - 0 9 +epd nrknqrbb/1p2ppp1/2pp4/Q6p/P2P3P/8/1PP1PPP1/NRKN1RBB w FBfb - perft 1 34 perft 2 513 perft 3 16111 @@ -2441,7 +2441,7 @@ perft 5 9569590 perft 6 206509331 id 271 -epd bbnrk1rq/pp2p1pp/2ppn3/5p2/8/3NNP1P/PPPPP1P1/BB1RK1RQ w GDgd - 1 9 +epd bbnrk1rq/pp2p1pp/2ppn3/5p2/8/3NNP1P/PPPPP1P1/BB1RK1RQ w GDgd - perft 1 28 perft 2 697 perft 3 20141 @@ -2450,7 +2450,7 @@ perft 5 15301879 perft 6 410843713 id 272 -epd bnrbknrq/ppppp2p/6p1/5p2/4QPP1/8/PPPPP2P/BNRBKNR1 w GCgc - 0 9 +epd bnrbknrq/ppppp2p/6p1/5p2/4QPP1/8/PPPPP2P/BNRBKNR1 w GCgc - perft 1 37 perft 2 901 perft 3 32612 @@ -2459,7 +2459,7 @@ perft 5 31385912 perft 6 903831981 id 273 -epd bnkrnbrq/ppppp1p1/B6p/5p2/8/4P3/PPPP1PPP/BNKRN1RQ w - - 0 9 +epd bnkrnbrq/ppppp1p1/B6p/5p2/8/4P3/PPPP1PPP/BNKRN1RQ w - - perft 1 26 perft 2 417 perft 3 11124 @@ -2468,7 +2468,7 @@ perft 5 5980981 perft 6 133080499 id 274 -epd bnrk1rqb/2pppp1p/3n4/pp4p1/3Q1P2/2N3P1/PPPPP2P/B1RKNR1B w FCfc - 0 9 +epd bnrk1rqb/2pppp1p/3n4/pp4p1/3Q1P2/2N3P1/PPPPP2P/B1RKNR1B w FCfc - perft 1 49 perft 2 1655 perft 3 74590 @@ -2477,7 +2477,7 @@ perft 5 107234294 perft 6 3651608327 id 275 -epd nbbrk1rq/pp2pppp/2pp4/8/2P2n2/6N1/PP1PP1PP/NBBRKR1Q w Dgd - 0 9 +epd nbbrk1rq/pp2pppp/2pp4/8/2P2n2/6N1/PP1PP1PP/NBBRKR1Q w Dgd - perft 1 28 perft 2 960 perft 3 26841 @@ -2486,7 +2486,7 @@ perft 5 26083252 perft 6 846682836 id 276 -epd nrbb2rq/pppk1ppp/4p1n1/3p4/6P1/1BP5/PP1PPPQP/NRB1KNR1 w GB - 0 9 +epd nrbb2rq/pppk1ppp/4p1n1/3p4/6P1/1BP5/PP1PPPQP/NRB1KNR1 w GB - perft 1 28 perft 2 735 perft 3 22048 @@ -2495,7 +2495,7 @@ perft 5 18588316 perft 6 512048946 id 277 -epd nrbk1brq/p1ppppp1/7p/1p6/4P1nP/P7/1PPP1PP1/NRBKNBRQ w GBgb - 0 9 +epd nrbk1brq/p1ppppp1/7p/1p6/4P1nP/P7/1PPP1PP1/NRBKNBRQ w GBgb - perft 1 22 perft 2 572 perft 3 12739 @@ -2504,7 +2504,7 @@ perft 5 8525056 perft 6 247615348 id 278 -epd nrbk1rqb/1pp2ppp/5n2/p2pp3/5B2/1N1P2P1/PPP1PP1P/1R1KNRQB w FBfb - 0 9 +epd nrbk1rqb/1pp2ppp/5n2/p2pp3/5B2/1N1P2P1/PPP1PP1P/1R1KNRQB w FBfb - perft 1 35 perft 2 927 perft 3 31559 @@ -2513,7 +2513,7 @@ perft 5 28465693 perft 6 783048748 id 279 -epd nbrkb1rq/p1pp1ppp/4n3/4p3/Pp6/6N1/1PPPPPPP/NBRKBRQ1 w Cgc - 0 9 +epd nbrkb1rq/p1pp1ppp/4n3/4p3/Pp6/6N1/1PPPPPPP/NBRKBRQ1 w Cgc - perft 1 20 perft 2 456 perft 3 10271 @@ -2522,7 +2522,7 @@ perft 5 6124625 perft 6 154766108 id 280 -epd nrkb1nrq/p2pp1pp/1pp2p2/7b/6PP/5P2/PPPPP2N/NRKBB1RQ w GBgb - 0 9 +epd nrkb1nrq/p2pp1pp/1pp2p2/7b/6PP/5P2/PPPPP2N/NRKBB1RQ w GBgb - perft 1 21 perft 2 479 perft 3 11152 @@ -2531,7 +2531,7 @@ perft 5 6696458 perft 6 165253524 id 281 -epd nr1nbbr1/pppkpp1p/6p1/3p4/P6P/1P6/1RPPPPP1/N1KNBBRQ w G - 1 9 +epd nr1nbbr1/pppkpp1p/6p1/3p4/P6P/1P6/1RPPPPP1/N1KNBBRQ w G - perft 1 20 perft 2 498 perft 3 11304 @@ -2540,7 +2540,7 @@ perft 5 7197322 perft 6 188021682 id 282 -epd nrknbrqb/3p1ppp/ppN1p3/8/6P1/8/PPPPPP1P/1RKNBRQB w FBfb - 0 9 +epd nrknbrqb/3p1ppp/ppN1p3/8/6P1/8/PPPPPP1P/1RKNBRQB w FBfb - perft 1 32 perft 2 526 perft 3 17267 @@ -2549,7 +2549,7 @@ perft 5 10755190 perft 6 220058991 id 283 -epd nbrkn1bq/p1pppr1p/1p6/5pp1/8/1N2PP2/PPPP2PP/1BKRNRBQ w c - 1 9 +epd nbrkn1bq/p1pppr1p/1p6/5pp1/8/1N2PP2/PPPP2PP/1BKRNRBQ w c - perft 1 19 perft 2 491 perft 3 10090 @@ -2558,7 +2558,7 @@ perft 5 6230616 perft 6 180748649 id 284 -epd nrkbnrbq/ppppppp1/8/8/7p/PP3P2/2PPPRPP/NRKBN1BQ w Bfb - 0 9 +epd nrkbnrbq/ppppppp1/8/8/7p/PP3P2/2PPPRPP/NRKBN1BQ w Bfb - perft 1 16 perft 2 353 perft 3 6189 @@ -2567,7 +2567,7 @@ perft 5 3008668 perft 6 82706705 id 285 -epd nrknrbbq/p4ppp/2p1p3/1p1p4/1P2P3/2P5/P1NP1PPP/1RKNRBBQ w EBeb - 0 9 +epd nrknrbbq/p4ppp/2p1p3/1p1p4/1P2P3/2P5/P1NP1PPP/1RKNRBBQ w EBeb - perft 1 29 perft 2 728 perft 3 21915 @@ -2576,7 +2576,7 @@ perft 5 18231199 perft 6 511686397 id 286 -epd nrknr1bb/pppp1p2/7p/2qPp1p1/8/1P5P/P1P1PPP1/NRKNRQBB w EBeb - 0 9 +epd nrknr1bb/pppp1p2/7p/2qPp1p1/8/1P5P/P1P1PPP1/NRKNRQBB w EBeb - perft 1 20 perft 2 714 perft 3 14336 @@ -2585,7 +2585,7 @@ perft 5 11132758 perft 6 386064577 id 287 -epd bbqnrrkn/ppp2p1p/3pp1p1/8/1PP5/2Q5/P1BPPPPP/B2NRKRN w GE - 0 9 +epd bbqnrrkn/ppp2p1p/3pp1p1/8/1PP5/2Q5/P1BPPPPP/B2NRKRN w GE - perft 1 39 perft 2 593 perft 3 23446 @@ -2594,7 +2594,7 @@ perft 5 16764576 perft 6 346185058 id 288 -epd bqn1rkrn/p1p2ppp/1p1p4/4p3/3PP2b/8/PPP2PPP/BQNBRKRN w GEge - 2 9 +epd bqn1rkrn/p1p2ppp/1p1p4/4p3/3PP2b/8/PPP2PPP/BQNBRKRN w GEge - perft 1 25 perft 2 773 perft 3 20042 @@ -2603,7 +2603,7 @@ perft 5 16632403 perft 6 515838333 id 289 -epd bqnrkb1n/p1p1pprp/3p4/1p2P1p1/2PP4/8/PP3PPP/BQNRKBRN w GDd - 1 9 +epd bqnrkb1n/p1p1pprp/3p4/1p2P1p1/2PP4/8/PP3PPP/BQNRKBRN w GDd - perft 1 31 perft 2 860 perft 3 28102 @@ -2612,7 +2612,7 @@ perft 5 27233018 perft 6 813751250 id 290 -epd bqr1krnb/ppppppp1/7p/3n4/1P4P1/P4N2/2PPPP1P/BQNRKR1B w FDf - 3 9 +epd bqr1krnb/ppppppp1/7p/3n4/1P4P1/P4N2/2PPPP1P/BQNRKR1B w FDf - perft 1 31 perft 2 709 perft 3 22936 @@ -2621,7 +2621,7 @@ perft 5 18608857 perft 6 480498340 id 291 -epd qbbn1krn/pp3ppp/4r3/2ppp3/P1P4P/8/1P1PPPP1/QBBNRKRN w GEg - 1 9 +epd qbbn1krn/pp3ppp/4r3/2ppp3/P1P4P/8/1P1PPPP1/QBBNRKRN w GEg - perft 1 26 perft 2 775 perft 3 21100 @@ -2630,7 +2630,7 @@ perft 5 18476807 perft 6 582542257 id 292 -epd qnbbrkrn/1p1pp2p/p7/2p2pp1/8/4P2P/PPPP1PPK/QNBBRR1N w ge - 0 9 +epd qnbbrkrn/1p1pp2p/p7/2p2pp1/8/4P2P/PPPP1PPK/QNBBRR1N w ge - perft 1 25 perft 2 599 perft 3 15139 @@ -2639,7 +2639,7 @@ perft 5 10260500 perft 6 279222412 id 293 -epd qnbrkbrn/1ppp2p1/p3p2p/5p2/P4P2/1P6/2PPP1PP/QNBRKBRN w GDgd - 0 9 +epd qnbrkbrn/1ppp2p1/p3p2p/5p2/P4P2/1P6/2PPP1PP/QNBRKBRN w GDgd - perft 1 27 perft 2 588 perft 3 16735 @@ -2648,7 +2648,7 @@ perft 5 11640416 perft 6 293541380 id 294 -epd 1nbrkrnb/p1pppp1p/1pq3p1/8/4P3/P1P4N/1P1P1PPP/QNBRKR1B w FDfd - 1 9 +epd 1nbrkrnb/p1pppp1p/1pq3p1/8/4P3/P1P4N/1P1P1PPP/QNBRKR1B w FDfd - perft 1 18 perft 2 609 perft 3 11789 @@ -2657,7 +2657,7 @@ perft 5 8604788 perft 6 299491047 id 295 -epd qb1r1krn/pppp2pp/1n2ppb1/4P3/7P/8/PPPP1PP1/QBNRBKRN w GDgd - 0 9 +epd qb1r1krn/pppp2pp/1n2ppb1/4P3/7P/8/PPPP1PP1/QBNRBKRN w GDgd - perft 1 20 perft 2 578 perft 3 12205 @@ -2666,7 +2666,7 @@ perft 5 7939483 perft 6 229142178 id 296 -epd qnr1bkrn/p3pppp/1bpp4/1p6/2P2PP1/8/PP1PPN1P/QNRBBKR1 w GCgc - 0 9 +epd qnr1bkrn/p3pppp/1bpp4/1p6/2P2PP1/8/PP1PPN1P/QNRBBKR1 w GCgc - perft 1 30 perft 2 865 perft 3 26617 @@ -2675,7 +2675,7 @@ perft 5 24475596 perft 6 719842237 id 297 -epd 1nkrbbrn/qppppppp/8/8/p2P4/1P5P/P1P1PPP1/QNKRBBRN w - - 0 9 +epd 1nkrbbrn/qppppppp/8/8/p2P4/1P5P/P1P1PPP1/QNKRBBRN w - - perft 1 27 perft 2 672 perft 3 18371 @@ -2684,7 +2684,7 @@ perft 5 14065717 perft 6 410130412 id 298 -epd 1qrkbrnb/ppp1p1pp/n2p4/5p2/4N3/8/PPPPPPPP/Q1RKBRNB w Ffc - 2 9 +epd 1qrkbrnb/ppp1p1pp/n2p4/5p2/4N3/8/PPPPPPPP/Q1RKBRNB w Ffc - perft 1 25 perft 2 718 perft 3 18573 @@ -2693,7 +2693,7 @@ perft 5 14404324 perft 6 424279467 id 299 -epd q1nrkrbn/pp1pppp1/2p4p/8/P7/5Pb1/BPPPPNPP/Q1NRKRB1 w FDfd - 0 9 +epd q1nrkrbn/pp1pppp1/2p4p/8/P7/5Pb1/BPPPPNPP/Q1NRKRB1 w FDfd - perft 1 22 perft 2 558 perft 3 12911 @@ -2702,7 +2702,7 @@ perft 5 8516966 perft 6 228074630 id 300 -epd qnrbkrbn/1p1p1pp1/p1p5/4p2p/8/3P1P2/PPP1P1PP/QNRBKRBN w FCfc - 0 9 +epd qnrbkrbn/1p1p1pp1/p1p5/4p2p/8/3P1P2/PPP1P1PP/QNRBKRBN w FCfc - perft 1 28 perft 2 669 perft 3 17713 @@ -2711,7 +2711,7 @@ perft 5 12055174 perft 6 313276304 id 301 -epd qnrkr1bn/p1pp1ppp/8/1p2p3/3P1P2/bP4P1/P1P1P2P/QNRKRBBN w ECec - 1 9 +epd qnrkr1bn/p1pp1ppp/8/1p2p3/3P1P2/bP4P1/P1P1P2P/QNRKRBBN w ECec - perft 1 23 perft 2 845 perft 3 20973 @@ -2720,7 +2720,7 @@ perft 5 19939053 perft 6 718075943 id 302 -epd q1krrnbb/p1p1pppp/2np4/1pB5/5P2/8/PPPPP1PP/QNRKRN1B w EC - 0 9 +epd q1krrnbb/p1p1pppp/2np4/1pB5/5P2/8/PPPPP1PP/QNRKRN1B w EC - perft 1 29 perft 2 776 perft 3 21966 @@ -2729,7 +2729,7 @@ perft 5 18110831 perft 6 549019739 id 303 -epd bbn1rkrn/pp1p1ppp/8/2p1p1q1/6P1/P7/BPPPPP1P/B1NQRKRN w GEge - 0 9 +epd bbn1rkrn/pp1p1ppp/8/2p1p1q1/6P1/P7/BPPPPP1P/B1NQRKRN w GEge - perft 1 26 perft 2 936 perft 3 25177 @@ -2738,7 +2738,7 @@ perft 5 24984621 perft 6 901444251 id 304 -epd bn1brkrn/pp1qpp1p/2p3p1/3p4/1PPP4/P7/4PPPP/BNQBRKRN w GEge - 1 9 +epd bn1brkrn/pp1qpp1p/2p3p1/3p4/1PPP4/P7/4PPPP/BNQBRKRN w GEge - perft 1 29 perft 2 755 perft 3 22858 @@ -2747,7 +2747,7 @@ perft 5 20128587 perft 6 600207069 id 305 -epd b2rkbrn/p1pppppp/qp6/8/1n6/2B2P2/P1PPP1PP/1NQRKBRN w GDgd - 0 9 +epd b2rkbrn/p1pppppp/qp6/8/1n6/2B2P2/P1PPP1PP/1NQRKBRN w GDgd - perft 1 24 perft 2 878 perft 3 21440 @@ -2756,7 +2756,7 @@ perft 5 20840078 perft 6 775795187 id 306 -epd b2rkrnb/pqp1pppp/n7/1p1p4/P7/N1P2N2/1P1PPPPP/B1QRKR1B w FDfd - 4 9 +epd b2rkrnb/pqp1pppp/n7/1p1p4/P7/N1P2N2/1P1PPPPP/B1QRKR1B w FDfd - perft 1 26 perft 2 724 perft 3 19558 @@ -2765,7 +2765,7 @@ perft 5 16109522 perft 6 492933398 id 307 -epd 1bbqrkrn/ppppp1p1/8/5p1p/P1n3P1/3P4/1PP1PP1P/NBBQRRKN w ge - 1 9 +epd 1bbqrkrn/ppppp1p1/8/5p1p/P1n3P1/3P4/1PP1PP1P/NBBQRRKN w ge - perft 1 25 perft 2 678 perft 3 17351 @@ -2774,7 +2774,7 @@ perft 5 12173245 perft 6 329661421 id 308 -epd nqb1rrkn/ppp1bppp/3pp3/8/3P4/1P6/PQP1PPPP/N1BBRRKN w - - 1 9 +epd nqb1rrkn/ppp1bppp/3pp3/8/3P4/1P6/PQP1PPPP/N1BBRRKN w - - perft 1 23 perft 2 503 perft 3 12465 @@ -2783,7 +2783,7 @@ perft 5 7626054 perft 6 188215608 id 309 -epd nqbrkbr1/p1pppppp/1p6/2N2n2/2P5/5P2/PP1PP1PP/1QBRKBRN w GDgd - 1 9 +epd nqbrkbr1/p1pppppp/1p6/2N2n2/2P5/5P2/PP1PP1PP/1QBRKBRN w GDgd - perft 1 29 perft 2 688 perft 3 20289 @@ -2792,7 +2792,7 @@ perft 5 15167248 perft 6 399015237 id 310 -epd nqbrkrn1/1ppppp2/6pp/p7/1P6/2Q5/P1PPPPPP/N1BRKRNB w FDfd - 0 9 +epd nqbrkrn1/1ppppp2/6pp/p7/1P6/2Q5/P1PPPPPP/N1BRKRNB w FDfd - perft 1 36 perft 2 602 perft 3 20985 @@ -2801,7 +2801,7 @@ perft 5 13706856 perft 6 291708797 id 311 -epd nbqrbrkn/pp1p1pp1/2p5/4p2p/2P3P1/1P3P2/P2PP2P/NBQRBKRN w GD - 0 9 +epd nbqrbrkn/pp1p1pp1/2p5/4p2p/2P3P1/1P3P2/P2PP2P/NBQRBKRN w GD - perft 1 34 perft 2 655 perft 3 22581 @@ -2810,7 +2810,7 @@ perft 5 16613630 perft 6 379344541 id 312 -epd nqrbbrkn/1p1pppp1/8/p1p4p/4P2P/1N4P1/PPPP1P2/1QRBBKRN w GC - 0 9 +epd nqrbbrkn/1p1pppp1/8/p1p4p/4P2P/1N4P1/PPPP1P2/1QRBBKRN w GC - perft 1 23 perft 2 597 perft 3 14468 @@ -2819,7 +2819,7 @@ perft 5 10096863 perft 6 294900903 id 313 -epd nqrkbbrn/2p1p1pp/pp1p1p2/8/P2N4/2P5/1P1PPPPP/1QRKBBRN w GCgc - 0 9 +epd nqrkbbrn/2p1p1pp/pp1p1p2/8/P2N4/2P5/1P1PPPPP/1QRKBBRN w GCgc - perft 1 32 perft 2 744 perft 3 23310 @@ -2828,7 +2828,7 @@ perft 5 17597164 perft 6 428786656 id 314 -epd n1krbrnb/q1pppppp/p7/1p6/3Q4/2P2P2/PP1PP1PP/N1RKBRNB w FC - 1 9 +epd n1krbrnb/q1pppppp/p7/1p6/3Q4/2P2P2/PP1PP1PP/N1RKBRNB w FC - perft 1 43 perft 2 1038 perft 3 41327 @@ -2837,7 +2837,7 @@ perft 5 40918952 perft 6 1126603824 id 315 -epd nb1rkrbn/p1pp1p1p/qp6/4p1p1/5PP1/P7/1PPPPB1P/NBQRKR1N w FDfd - 2 9 +epd nb1rkrbn/p1pp1p1p/qp6/4p1p1/5PP1/P7/1PPPPB1P/NBQRKR1N w FDfd - perft 1 26 perft 2 645 perft 3 16463 @@ -2846,7 +2846,7 @@ perft 5 11911314 perft 6 342563372 id 316 -epd nqr1krbn/pppp1ppp/8/8/3pP3/5P2/PPPb1NPP/NQRBKRB1 w FCfc - 3 9 +epd nqr1krbn/pppp1ppp/8/8/3pP3/5P2/PPPb1NPP/NQRBKRB1 w FCfc - perft 1 2 perft 2 51 perft 3 1047 @@ -2855,7 +2855,7 @@ perft 5 612305 perft 6 17040200 id 317 -epd n1rkrbbn/pqppppp1/7p/1p6/8/1NPP4/PP1KPPPP/1QR1RBBN w ec - 0 9 +epd n1rkrbbn/pqppppp1/7p/1p6/8/1NPP4/PP1KPPPP/1QR1RBBN w ec - perft 1 25 perft 2 674 perft 3 17553 @@ -2864,7 +2864,7 @@ perft 5 13421727 perft 6 403551903 id 318 -epd 1qrkrnbb/1p1p1ppp/pnp1p3/8/3PP3/P6P/1PP2PP1/NQRKRNBB w ECec - 0 9 +epd 1qrkrnbb/1p1p1ppp/pnp1p3/8/3PP3/P6P/1PP2PP1/NQRKRNBB w ECec - perft 1 24 perft 2 688 perft 3 17342 @@ -2873,7 +2873,7 @@ perft 5 13322502 perft 6 403441498 id 319 -epd 1bnrqkrn/2ppppp1/p7/1p1b3p/3PP1P1/8/PPPQ1P1P/BBNR1KRN w GDgd - 1 9 +epd 1bnrqkrn/2ppppp1/p7/1p1b3p/3PP1P1/8/PPPQ1P1P/BBNR1KRN w GDgd - perft 1 35 perft 2 925 perft 3 32238 @@ -2882,7 +2882,7 @@ perft 5 30458921 perft 6 824344087 id 320 -epd bnrbqkr1/ppp2pp1/6n1/3pp2p/1P6/2N3N1/P1PPPPPP/B1RBQRK1 w gc - 0 9 +epd bnrbqkr1/ppp2pp1/6n1/3pp2p/1P6/2N3N1/P1PPPPPP/B1RBQRK1 w gc - perft 1 23 perft 2 704 perft 3 17345 @@ -2891,7 +2891,7 @@ perft 5 14154852 perft 6 450893738 id 321 -epd 1nrqkbrn/p1pppppp/8/1p1b4/P6P/5P2/1PPPP1P1/BNRQKBRN w GCgc - 1 9 +epd 1nrqkbrn/p1pppppp/8/1p1b4/P6P/5P2/1PPPP1P1/BNRQKBRN w GCgc - perft 1 19 perft 2 505 perft 3 10619 @@ -2900,7 +2900,7 @@ perft 5 6450025 perft 6 175593967 id 322 -epd b1rqkrnb/ppppppp1/8/6p1/3n4/NP6/P1PPPP1P/B1RQKRNB w FCfc - 0 9 +epd b1rqkrnb/ppppppp1/8/6p1/3n4/NP6/P1PPPP1P/B1RQKRNB w FCfc - perft 1 25 perft 2 614 perft 3 15578 @@ -2909,7 +2909,7 @@ perft 5 10391021 perft 6 259629603 id 323 -epd nbbrqkrn/ppp3p1/3pp3/5p1p/1P2P3/P7/2PPQPPP/NBBR1KRN w GDgd - 0 9 +epd nbbrqkrn/ppp3p1/3pp3/5p1p/1P2P3/P7/2PPQPPP/NBBR1KRN w GDgd - perft 1 30 perft 2 833 perft 3 25719 @@ -2918,7 +2918,7 @@ perft 5 22873901 perft 6 649556666 id 324 -epd nr1bqrk1/ppp1pppp/6n1/3pP3/8/5PQb/PPPP2PP/NRBB1KRN w GB - 3 9 +epd nr1bqrk1/ppp1pppp/6n1/3pP3/8/5PQb/PPPP2PP/NRBB1KRN w GB - perft 1 26 perft 2 734 perft 3 20161 @@ -2927,7 +2927,7 @@ perft 5 17199594 perft 6 512134836 id 325 -epd 1rbqkbr1/ppppp1pp/1n6/4np2/3P1P2/6P1/PPPQP2P/NRB1KBRN w GBgb - 1 9 +epd 1rbqkbr1/ppppp1pp/1n6/4np2/3P1P2/6P1/PPPQP2P/NRB1KBRN w GBgb - perft 1 27 perft 2 662 perft 3 17897 @@ -2936,7 +2936,7 @@ perft 5 13038519 perft 6 338365642 id 326 -epd nr1qkr1b/ppp1pp1p/4bn2/3p2p1/4P3/1Q6/PPPP1PPP/NRB1KRNB w FBfb - 4 9 +epd nr1qkr1b/ppp1pp1p/4bn2/3p2p1/4P3/1Q6/PPPP1PPP/NRB1KRNB w FBfb - perft 1 33 perft 2 939 perft 3 30923 @@ -2945,7 +2945,7 @@ perft 5 30995969 perft 6 991509814 id 327 -epd nb1qbkrn/pprp1pp1/7p/2p1pB2/Q1PP4/8/PP2PPPP/N1R1BKRN w GCg - 2 9 +epd nb1qbkrn/pprp1pp1/7p/2p1pB2/Q1PP4/8/PP2PPPP/N1R1BKRN w GCg - perft 1 47 perft 2 1128 perft 3 50723 @@ -2954,7 +2954,7 @@ perft 5 56747878 perft 6 1560584212 id 328 -epd nrqb1rkn/pp2pppp/2bp4/2p5/6P1/2P3N1/PP1PPP1P/NRQBBRK1 w - - 3 9 +epd nrqb1rkn/pp2pppp/2bp4/2p5/6P1/2P3N1/PP1PPP1P/NRQBBRK1 w - - perft 1 24 perft 2 828 perft 3 21148 @@ -2963,7 +2963,7 @@ perft 5 19506135 perft 6 668969549 id 329 -epd nrq1bbrn/ppkpp2p/2p3p1/P4p2/8/4P1N1/1PPP1PPP/NRQKBBR1 w GB - 0 9 +epd nrq1bbrn/ppkpp2p/2p3p1/P4p2/8/4P1N1/1PPP1PPP/NRQKBBR1 w GB - perft 1 25 perft 2 525 perft 3 13533 @@ -2972,7 +2972,7 @@ perft 5 8250997 perft 6 201795680 id 330 -epd Br1kbrn1/pqpppp2/8/6pp/3b2P1/1N6/PPPPPP1P/1RQKBRN1 w FBfb - 3 9 +epd Br1kbrn1/pqpppp2/8/6pp/3b2P1/1N6/PPPPPP1P/1RQKBRN1 w FBfb - perft 1 20 perft 2 790 perft 3 18175 @@ -2981,7 +2981,7 @@ perft 5 17735648 perft 6 669854148 id 331 -epd nbrqkrbn/2p1p1pp/p7/1p1p1p2/4P1P1/5P2/PPPP3P/NBRQKRBN w FCfc - 0 9 +epd nbrqkrbn/2p1p1pp/p7/1p1p1p2/4P1P1/5P2/PPPP3P/NBRQKRBN w FCfc - perft 1 29 perft 2 771 perft 3 22489 @@ -2990,7 +2990,7 @@ perft 5 19192982 perft 6 591335970 id 332 -epd 1rqbkrbn/1ppppp1p/1n6/p1N3p1/8/2P4P/PP1PPPP1/1RQBKRBN w FBfb - 0 9 +epd 1rqbkrbn/1ppppp1p/1n6/p1N3p1/8/2P4P/PP1PPPP1/1RQBKRBN w FBfb - perft 1 29 perft 2 502 perft 3 14569 @@ -2999,7 +2999,7 @@ perft 5 8652810 perft 6 191762235 id 333 -epd 1rqkrbbn/ppnpp1pp/8/2p5/6p1/3P4/PPP1PPPP/NRK1RBBN w eb - 0 9 +epd 1rqkrbbn/ppnpp1pp/8/2p5/6p1/3P4/PPP1PPPP/NRK1RBBN w eb - perft 1 19 perft 2 531 perft 3 10812 @@ -3008,7 +3008,7 @@ perft 5 6506674 perft 6 184309316 id 334 -epd nrqkrnbb/p1pp2pp/5p2/4P3/2p5/4N3/PP1PP1PP/NRQKR1BB w EBeb - 0 9 +epd nrqkrnbb/p1pp2pp/5p2/4P3/2p5/4N3/PP1PP1PP/NRQKR1BB w EBeb - perft 1 26 perft 2 800 perft 3 23256 @@ -3017,7 +3017,7 @@ perft 5 23952941 perft 6 809841274 id 335 -epd bbnrkqrn/pp3pp1/4p2p/2pp4/4P1P1/1PB5/P1PP1P1P/1BNRKQRN w GDgd - 0 9 +epd bbnrkqrn/pp3pp1/4p2p/2pp4/4P1P1/1PB5/P1PP1P1P/1BNRKQRN w GDgd - perft 1 33 perft 2 915 perft 3 30536 @@ -3026,7 +3026,7 @@ perft 5 29602610 perft 6 881898159 id 336 -epd bnrbkqr1/1p2pppp/6n1/p1pp4/7P/P3P3/1PPPKPP1/BNRB1QRN w gc - 0 9 +epd bnrbkqr1/1p2pppp/6n1/p1pp4/7P/P3P3/1PPPKPP1/BNRB1QRN w gc - perft 1 19 perft 2 457 perft 3 9332 @@ -3035,7 +3035,7 @@ perft 5 5356253 perft 6 144653627 id 337 -epd b1rkqbrn/pp1p2pp/2n1p3/2p2p2/3P2PP/8/PPP1PP2/BNKRQBRN w gc - 0 9 +epd b1rkqbrn/pp1p2pp/2n1p3/2p2p2/3P2PP/8/PPP1PP2/BNKRQBRN w gc - perft 1 30 perft 2 985 perft 3 30831 @@ -3044,7 +3044,7 @@ perft 5 32684185 perft 6 1080607773 id 338 -epd b1rkqrnb/2ppppp1/np6/p6p/1P6/P2P3P/2P1PPP1/BNRKQRNB w FCfc - 0 9 +epd b1rkqrnb/2ppppp1/np6/p6p/1P6/P2P3P/2P1PPP1/BNRKQRNB w FCfc - perft 1 26 perft 2 692 perft 3 18732 @@ -3053,7 +3053,7 @@ perft 5 14561181 perft 6 413226841 id 339 -epd nbbrkqrn/1ppp1p2/p6p/4p1p1/5P2/1P5P/P1PPPNP1/NBBRKQR1 w GDgd - 0 9 +epd nbbrkqrn/1ppp1p2/p6p/4p1p1/5P2/1P5P/P1PPPNP1/NBBRKQR1 w GDgd - perft 1 22 perft 2 561 perft 3 13222 @@ -3062,7 +3062,7 @@ perft 5 9307003 perft 6 273928315 id 340 -epd nrbbkqrn/p1pppppp/8/1p6/4P3/7Q/PPPP1PPP/NRBBK1RN w GBgb - 0 9 +epd nrbbkqrn/p1pppppp/8/1p6/4P3/7Q/PPPP1PPP/NRBBK1RN w GBgb - perft 1 38 perft 2 769 perft 3 28418 @@ -3071,7 +3071,7 @@ perft 5 23091070 perft 6 560139600 id 341 -epd nrbkqbrn/1pppp2p/8/p4pp1/P4PQ1/8/1PPPP1PP/NRBK1BRN w GBgb - 0 9 +epd nrbkqbrn/1pppp2p/8/p4pp1/P4PQ1/8/1PPPP1PP/NRBK1BRN w GBgb - perft 1 23 perft 2 507 perft 3 13067 @@ -3080,7 +3080,7 @@ perft 5 8887567 perft 6 237475184 id 342 -epd nr1kqr1b/pp2pppp/5n2/2pp4/P5b1/5P2/1PPPPRPP/NRBK1QNB w Bfb - 2 9 +epd nr1kqr1b/pp2pppp/5n2/2pp4/P5b1/5P2/1PPPPRPP/NRBK1QNB w Bfb - perft 1 18 perft 2 626 perft 3 12386 @@ -3089,7 +3089,7 @@ perft 5 9465555 perft 6 335004239 id 343 -epd nbkrbqrn/1pppppp1/8/4P2p/pP6/P7/2PP1PPP/NBRKBQRN w GC - 0 9 +epd nbkrbqrn/1pppppp1/8/4P2p/pP6/P7/2PP1PPP/NBRKBQRN w GC - perft 1 22 perft 2 329 perft 3 8475 @@ -3098,7 +3098,7 @@ perft 5 4160034 perft 6 82875306 id 344 -epd nrkb1qrn/pp1pp1pp/8/5p1b/P1p4P/6N1/1PPPPPP1/NRKBBQR1 w GBgb - 2 9 +epd nrkb1qrn/pp1pp1pp/8/5p1b/P1p4P/6N1/1PPPPPP1/NRKBBQR1 w GBgb - perft 1 16 perft 2 479 perft 3 9037 @@ -3107,7 +3107,7 @@ perft 5 5862341 perft 6 184959796 id 345 -epd 1rkq1brn/ppppp1pp/1n6/3b1p2/3N3P/5P2/PPPPP1P1/1RKQBBRN w GBgb - 3 9 +epd 1rkq1brn/ppppp1pp/1n6/3b1p2/3N3P/5P2/PPPPP1P1/1RKQBBRN w GBgb - perft 1 23 perft 2 614 perft 3 15324 @@ -3116,7 +3116,7 @@ perft 5 11090645 perft 6 313526088 id 346 -epd nrk1brnb/pp1ppppp/2p5/3q4/5P2/PP6/1KPPP1PP/NR1QBRNB w fb - 1 9 +epd nrk1brnb/pp1ppppp/2p5/3q4/5P2/PP6/1KPPP1PP/NR1QBRNB w fb - perft 1 25 perft 2 942 perft 3 21765 @@ -3125,7 +3125,7 @@ perft 5 19318837 perft 6 685549171 id 347 -epd nbrkqr1n/1pppp2p/p4pp1/2Bb4/5P2/6P1/PPPPP2P/NBRKQ1RN w Cfc - 2 9 +epd nbrkqr1n/1pppp2p/p4pp1/2Bb4/5P2/6P1/PPPPP2P/NBRKQ1RN w Cfc - perft 1 30 perft 2 841 perft 3 24775 @@ -3134,7 +3134,7 @@ perft 5 20145765 perft 6 557578726 id 348 -epd n1kbqrbn/2p1pppp/1r6/pp1p4/P7/3P4/1PP1PPPP/NRKBQRBN w FBf - 2 9 +epd n1kbqrbn/2p1pppp/1r6/pp1p4/P7/3P4/1PP1PPPP/NRKBQRBN w FBf - perft 1 21 perft 2 591 perft 3 14101 @@ -3143,7 +3143,7 @@ perft 5 10295086 perft 6 292131422 id 349 -epd nrkqrbb1/ppp1pppp/3p4/8/4P3/2Pn1P2/PP4PP/NRKQRBBN w EBeb - 0 9 +epd nrkqrbb1/ppp1pppp/3p4/8/4P3/2Pn1P2/PP4PP/NRKQRBBN w EBeb - perft 1 4 perft 2 88 perft 3 3090 @@ -3152,7 +3152,7 @@ perft 5 2640555 perft 6 66958031 id 350 -epd nrkqrnbb/ppppp1p1/7p/1P3p2/3P4/2P5/P3PPPP/NRKQRNBB w EBeb - 0 9 +epd nrkqrnbb/ppppp1p1/7p/1P3p2/3P4/2P5/P3PPPP/NRKQRNBB w EBeb - perft 1 29 perft 2 689 perft 3 21091 @@ -3161,7 +3161,7 @@ perft 5 16226660 perft 6 408570219 id 351 -epd bbnr1rqn/pp2pkpp/2pp1p2/8/4P1P1/8/PPPP1P1P/BBNRKRQN w FD - 0 9 +epd bbnr1rqn/pp2pkpp/2pp1p2/8/4P1P1/8/PPPP1P1P/BBNRKRQN w FD - perft 1 21 perft 2 463 perft 3 11135 @@ -3170,7 +3170,7 @@ perft 5 6826249 perft 6 165025370 id 352 -epd bnrbk1qn/1pppprpp/8/p4p1P/6P1/3P4/PPP1PP2/BNRBKRQN w FCc - 0 9 +epd bnrbk1qn/1pppprpp/8/p4p1P/6P1/3P4/PPP1PP2/BNRBKRQN w FCc - perft 1 22 perft 2 459 perft 3 11447 @@ -3179,7 +3179,7 @@ perft 5 7371098 perft 6 190583454 id 353 -epd 1nrkrbqn/p1pp1ppp/4p3/1p6/1PP5/6PB/P2PPPbP/BNRKR1QN w ECec - 0 9 +epd 1nrkrbqn/p1pp1ppp/4p3/1p6/1PP5/6PB/P2PPPbP/BNRKR1QN w ECec - perft 1 30 perft 2 931 perft 3 29012 @@ -3188,7 +3188,7 @@ perft 5 28412902 perft 6 869228014 id 354 -epd b1rkr1nb/pppppqp1/n4B2/7p/8/1P4P1/P1PPPP1P/1NKRRQNB w ec - 1 9 +epd b1rkr1nb/pppppqp1/n4B2/7p/8/1P4P1/P1PPPP1P/1NKRRQNB w ec - perft 1 36 perft 2 934 perft 3 31790 @@ -3197,7 +3197,7 @@ perft 5 30392925 perft 6 952871799 id 355 -epd nbbrkrqn/p1ppp1p1/8/1p3p1p/2P3PP/8/PP1PPPQ1/NBBRKR1N w FDfd - 0 9 +epd nbbrkrqn/p1ppp1p1/8/1p3p1p/2P3PP/8/PP1PPPQ1/NBBRKR1N w FDfd - perft 1 34 perft 2 938 perft 3 31848 @@ -3206,7 +3206,7 @@ perft 5 31185844 perft 6 944483246 id 356 -epd 1rbbkrqn/ppp1pp2/1n1p2p1/7p/P3P1P1/3P4/1PP2P1P/NRBBKRQN w FBfb - 0 9 +epd 1rbbkrqn/ppp1pp2/1n1p2p1/7p/P3P1P1/3P4/1PP2P1P/NRBBKRQN w FBfb - perft 1 26 perft 2 646 perft 3 18083 @@ -3215,7 +3215,7 @@ perft 5 14006203 perft 6 384101783 id 357 -epd nrbkrbq1/Qpppp1pp/2n5/5p2/P4P2/6N1/1PPPP1PP/NRBKRB2 w EBeb - 1 9 +epd nrbkrbq1/Qpppp1pp/2n5/5p2/P4P2/6N1/1PPPP1PP/NRBKRB2 w EBeb - perft 1 27 perft 2 619 perft 3 16713 @@ -3224,7 +3224,7 @@ perft 5 11718463 perft 6 313794027 id 358 -epd 1rbkr1nb/pppp1qpp/1n6/4pp2/1PP1P3/8/PB1P1PPP/NR1KRQNB w EBeb - 1 9 +epd 1rbkr1nb/pppp1qpp/1n6/4pp2/1PP1P3/8/PB1P1PPP/NR1KRQNB w EBeb - perft 1 32 perft 2 1029 perft 3 32970 @@ -3233,7 +3233,7 @@ perft 5 35483796 perft 6 1181835398 id 359 -epd nbrk1rqn/p1ppp2p/1p6/5ppb/8/1N2P2P/PPPP1PP1/1BKRBRQN w fc - 0 9 +epd nbrk1rqn/p1ppp2p/1p6/5ppb/8/1N2P2P/PPPP1PP1/1BKRBRQN w fc - perft 1 18 perft 2 594 perft 3 12350 @@ -3242,7 +3242,7 @@ perft 5 9329122 perft 6 315021712 id 360 -epd nrkbbrqn/3pppp1/7p/ppp5/P7/1N5P/1PPPPPP1/1RKBBRQN w FBfb - 0 9 +epd nrkbbrqn/3pppp1/7p/ppp5/P7/1N5P/1PPPPPP1/1RKBBRQN w FBfb - perft 1 19 perft 2 417 perft 3 9026 @@ -3251,7 +3251,7 @@ perft 5 5236331 perft 6 137024458 id 361 -epd nrkr1bqn/ppp1pppp/3p4/1b6/7P/P7/1PPPPPP1/NRKRBBQN w DBdb - 1 9 +epd nrkr1bqn/ppp1pppp/3p4/1b6/7P/P7/1PPPPPP1/NRKRBBQN w DBdb - perft 1 17 perft 2 457 perft 3 9083 @@ -3260,7 +3260,7 @@ perft 5 5503579 perft 6 150091997 id 362 -epd nrkrbqnb/p4ppp/1p2p3/2pp4/6P1/2P2N2/PPNPPP1P/1RKRBQ1B w DBdb - 0 9 +epd nrkrbqnb/p4ppp/1p2p3/2pp4/6P1/2P2N2/PPNPPP1P/1RKRBQ1B w DBdb - perft 1 27 perft 2 755 perft 3 21012 @@ -3269,7 +3269,7 @@ perft 5 17883987 perft 6 547233320 id 363 -epd nbkrr1bn/ppB2ppp/4p3/2qp4/4P3/5P2/PPPP2PP/NBRKRQ1N w EC - 1 9 +epd nbkrr1bn/ppB2ppp/4p3/2qp4/4P3/5P2/PPPP2PP/NBRKRQ1N w EC - perft 1 37 perft 2 1473 perft 3 51939 @@ -3278,7 +3278,7 @@ perft 5 68070015 perft 6 2490912491 id 364 -epd n1kbrqbn/p1pp1pp1/4p2p/2B5/1r3P2/8/PPPPP1PP/NRKBRQ1N w EBe - 2 9 +epd n1kbrqbn/p1pp1pp1/4p2p/2B5/1r3P2/8/PPPPP1PP/NRKBRQ1N w EBe - perft 1 30 perft 2 1029 perft 3 30874 @@ -3287,7 +3287,7 @@ perft 5 32318550 perft 6 1106487743 id 365 -epd nrkrqbbn/2pppp1p/8/pp6/1P1P2p1/P5P1/2P1PP1P/NRKRQBBN w DBdb - 0 9 +epd nrkrqbbn/2pppp1p/8/pp6/1P1P2p1/P5P1/2P1PP1P/NRKRQBBN w DBdb - perft 1 22 perft 2 421 perft 3 10034 @@ -3296,7 +3296,7 @@ perft 5 5754555 perft 6 141245633 id 366 -epd nrkr1nbb/1ppp2pp/p3q3/4pp2/2P5/P3P3/1PKP1PPP/NR1RQNBB w db - 0 9 +epd nrkr1nbb/1ppp2pp/p3q3/4pp2/2P5/P3P3/1PKP1PPP/NR1RQNBB w db - perft 1 22 perft 2 619 perft 3 13953 @@ -3305,7 +3305,7 @@ perft 5 9905109 perft 6 301403003 id 367 -epd bbnrkrnq/1pp1p2p/6p1/p2p1p2/8/1P2P3/P1PP1PPP/BBNRKRNQ w FDfd - 0 9 +epd bbnrkrnq/1pp1p2p/6p1/p2p1p2/8/1P2P3/P1PP1PPP/BBNRKRNQ w FDfd - perft 1 27 perft 2 805 perft 3 21915 @@ -3314,7 +3314,7 @@ perft 5 19133881 perft 6 620749189 id 368 -epd bnrbkrn1/pp1ppp2/2p3pp/8/2Pq4/P4PP1/1P1PP2P/BNRBKRNQ w FCfc - 1 9 +epd bnrbkrn1/pp1ppp2/2p3pp/8/2Pq4/P4PP1/1P1PP2P/BNRBKRNQ w FCfc - perft 1 20 perft 2 770 perft 3 16593 @@ -3323,7 +3323,7 @@ perft 5 13581691 perft 6 456736500 id 369 -epd b1rkrbnq/1pp1pppp/2np4/p5N1/8/1P2P3/P1PP1PPP/BNRKRB1Q w ECec - 0 9 +epd b1rkrbnq/1pp1pppp/2np4/p5N1/8/1P2P3/P1PP1PPP/BNRKRB1Q w ECec - perft 1 37 perft 2 740 perft 3 27073 @@ -3332,7 +3332,7 @@ perft 5 21156664 perft 6 485803600 id 370 -epd b1krrnqb/pp1ppp1p/n1p3p1/2N5/6P1/8/PPPPPP1P/B1RKRNQB w EC - 0 9 +epd b1krrnqb/pp1ppp1p/n1p3p1/2N5/6P1/8/PPPPPP1P/B1RKRNQB w EC - perft 1 34 perft 2 850 perft 3 28494 @@ -3341,7 +3341,7 @@ perft 5 25360295 perft 6 698159474 id 371 -epd 1bbr1rnq/ppppkppp/8/3np3/4P3/3P4/PPP1KPPP/NBBRR1NQ w - - 1 9 +epd 1bbr1rnq/ppppkppp/8/3np3/4P3/3P4/PPP1KPPP/NBBRR1NQ w - - perft 1 27 perft 2 704 perft 3 18290 @@ -3350,7 +3350,7 @@ perft 5 12817011 perft 6 341026662 id 372 -epd nrbbk1nq/p1p1prpp/1p6/N2p1p2/P7/8/1PPPPPPP/R1BBKRNQ w Fb - 2 9 +epd nrbbk1nq/p1p1prpp/1p6/N2p1p2/P7/8/1PPPPPPP/R1BBKRNQ w Fb - perft 1 23 perft 2 552 perft 3 13710 @@ -3359,7 +3359,7 @@ perft 5 9236564 perft 6 248469879 id 373 -epd 1rbkrb1q/1pppp1pp/1n5n/p4p2/P3P3/1P6/2PPNPPP/NRBKRB1Q w EBeb - 1 9 +epd 1rbkrb1q/1pppp1pp/1n5n/p4p2/P3P3/1P6/2PPNPPP/NRBKRB1Q w EBeb - perft 1 22 perft 2 415 perft 3 10198 @@ -3368,7 +3368,7 @@ perft 5 5735644 perft 6 135295774 id 374 -epd nrbkr1qb/1pp1pppp/6n1/p2p4/2P1P3/1N4N1/PP1P1PPP/1RBKR1QB w EBeb - 0 9 +epd nrbkr1qb/1pp1pppp/6n1/p2p4/2P1P3/1N4N1/PP1P1PPP/1RBKR1QB w EBeb - perft 1 27 perft 2 709 perft 3 19126 @@ -3377,7 +3377,7 @@ perft 5 14192779 perft 6 380516508 id 375 -epd nbrkbrnq/p3p1pp/1pp2p2/3p4/1PP5/4P3/P1KP1PPP/NBR1BRNQ w fc - 0 9 +epd nbrkbrnq/p3p1pp/1pp2p2/3p4/1PP5/4P3/P1KP1PPP/NBR1BRNQ w fc - perft 1 24 perft 2 715 perft 3 18009 @@ -3386,7 +3386,7 @@ perft 5 14322279 perft 6 427269976 id 376 -epd nrk1brnq/pp1p1pp1/7p/b1p1p3/1P6/6P1/P1PPPPQP/NRKBBRN1 w FBfb - 2 9 +epd nrk1brnq/pp1p1pp1/7p/b1p1p3/1P6/6P1/P1PPPPQP/NRKBBRN1 w FBfb - perft 1 29 perft 2 675 perft 3 20352 @@ -3395,7 +3395,7 @@ perft 5 15316285 perft 6 389051744 id 377 -epd nrkr1bnq/1p2pppp/p2p4/1bp5/PP6/1R5N/2PPPPPP/N1KRBB1Q w Ddb - 2 9 +epd nrkr1bnq/1p2pppp/p2p4/1bp5/PP6/1R5N/2PPPPPP/N1KRBB1Q w Ddb - perft 1 27 perft 2 744 perft 3 20494 @@ -3404,7 +3404,7 @@ perft 5 16188945 perft 6 458900901 id 378 -epd nrk1b1qb/pppn1ppp/3rp3/3p4/2P3P1/3P4/PPN1PP1P/1RKRBNQB w DBb - 3 9 +epd nrk1b1qb/pppn1ppp/3rp3/3p4/2P3P1/3P4/PPN1PP1P/1RKRBNQB w DBb - perft 1 35 perft 2 941 perft 3 33203 @@ -3413,7 +3413,7 @@ perft 5 33150360 perft 6 968024386 id 379 -epd nb1rrnbq/ppkp1ppp/8/2p1p3/P7/1N2P3/1PPP1PPP/1BKRRNBQ w - - 1 9 +epd nb1rrnbq/ppkp1ppp/8/2p1p3/P7/1N2P3/1PPP1PPP/1BKRRNBQ w - - perft 1 19 perft 2 451 perft 3 9655 @@ -3422,7 +3422,7 @@ perft 5 5506897 perft 6 139436165 id 380 -epd nrkbrnbq/4pppp/1ppp4/p7/2P1P3/3P2N1/PP3PPP/NRKBR1BQ w EBeb - 0 9 +epd nrkbrnbq/4pppp/1ppp4/p7/2P1P3/3P2N1/PP3PPP/NRKBR1BQ w EBeb - perft 1 29 perft 2 591 perft 3 17132 @@ -3431,7 +3431,7 @@ perft 5 11245508 perft 6 270967202 id 381 -epd nrkrnbbq/3p1ppp/1p6/p1p1p3/3P2P1/P4Q2/1PP1PP1P/NRKRNBB1 w DBdb - 0 9 +epd nrkrnbbq/3p1ppp/1p6/p1p1p3/3P2P1/P4Q2/1PP1PP1P/NRKRNBB1 w DBdb - perft 1 38 perft 2 792 perft 3 28597 @@ -3440,7 +3440,7 @@ perft 5 22654797 perft 6 540864616 id 382 -epd nr1rnqbb/ppp1pp1p/3k2p1/3p4/1P5P/3P1N2/P1P1PPP1/NRKR1QBB w DB - 1 9 +epd nr1rnqbb/ppp1pp1p/3k2p1/3p4/1P5P/3P1N2/P1P1PPP1/NRKR1QBB w DB - perft 1 25 perft 2 758 perft 3 18547 @@ -3449,7 +3449,7 @@ perft 5 13890077 perft 6 402109399 id 383 -epd bbqrnnkr/1ppp1p1p/5p2/p5p1/P7/1P4P1/2PPPP1P/1BQRNNKR w HDhd - 0 9 +epd bbqrnnkr/1ppp1p1p/5p2/p5p1/P7/1P4P1/2PPPP1P/1BQRNNKR w HDhd - perft 1 20 perft 2 322 perft 3 7224 @@ -3458,7 +3458,7 @@ perft 5 3588435 perft 6 82754650 id 384 -epd bqrb2k1/pppppppr/5nnp/8/3P1P2/4P1N1/PPP3PP/BQRBN1KR w HCc - 1 9 +epd bqrb2k1/pppppppr/5nnp/8/3P1P2/4P1N1/PPP3PP/BQRBN1KR w HCc - perft 1 25 perft 2 597 perft 3 15872 @@ -3467,7 +3467,7 @@ perft 5 11162476 perft 6 295682250 id 385 -epd bqrnn1kr/1pppbppp/8/4p3/1p6/2P1N2P/P2PPPP1/BQR1NBKR w HChc - 1 9 +epd bqrnn1kr/1pppbppp/8/4p3/1p6/2P1N2P/P2PPPP1/BQR1NBKR w HChc - perft 1 34 perft 2 921 perft 3 31695 @@ -3476,7 +3476,7 @@ perft 5 30126510 perft 6 850296236 id 386 -epd bqr1nkr1/pppppp2/2n3p1/7p/1P1b1P2/8/PQP1P1PP/B1RNNKRB w GCgc - 0 9 +epd bqr1nkr1/pppppp2/2n3p1/7p/1P1b1P2/8/PQP1P1PP/B1RNNKRB w GCgc - perft 1 23 perft 2 788 perft 3 21539 @@ -3485,7 +3485,7 @@ perft 5 20849374 perft 6 645694580 id 387 -epd qbbrnn1r/1pppp1pk/p7/5p1p/P2P3P/3N4/1PP1PPP1/QBBR1NKR w HD - 0 9 +epd qbbrnn1r/1pppp1pk/p7/5p1p/P2P3P/3N4/1PP1PPP1/QBBR1NKR w HD - perft 1 34 perft 2 713 perft 3 24475 @@ -3494,7 +3494,7 @@ perft 5 19494094 perft 6 482645160 id 388 -epd qrbb2kr/p1pppppp/1p1n4/8/1P3n2/P7/Q1PPP1PP/1RBBNNKR w HBhb - 0 9 +epd qrbb2kr/p1pppppp/1p1n4/8/1P3n2/P7/Q1PPP1PP/1RBBNNKR w HBhb - perft 1 28 perft 2 977 perft 3 26955 @@ -3503,7 +3503,7 @@ perft 5 27802999 perft 6 992109168 id 389 -epd qrb2bkr/1pp1pppp/2np1n2/pN6/3P4/4B3/PPP1PPPP/QR2NBKR w HBhb - 0 9 +epd qrb2bkr/1pp1pppp/2np1n2/pN6/3P4/4B3/PPP1PPPP/QR2NBKR w HBhb - perft 1 27 perft 2 730 perft 3 20534 @@ -3512,7 +3512,7 @@ perft 5 17005916 perft 6 507008968 id 390 -epd qrbnnkrb/pp2pp1p/8/2pp2p1/7P/P1P5/QP1PPPP1/1RBNNKRB w GBgb - 0 9 +epd qrbnnkrb/pp2pp1p/8/2pp2p1/7P/P1P5/QP1PPPP1/1RBNNKRB w GBgb - perft 1 24 perft 2 813 perft 3 21142 @@ -3521,7 +3521,7 @@ perft 5 19615756 perft 6 655850285 id 391 -epd 1brnb1kr/p1pppppp/1p6/8/4q2n/1P2P1P1/PNPP1P1P/QBR1BNKR w HChc - 3 9 +epd 1brnb1kr/p1pppppp/1p6/8/4q2n/1P2P1P1/PNPP1P1P/QBR1BNKR w HChc - perft 1 17 perft 2 734 perft 3 13462 @@ -3530,7 +3530,7 @@ perft 5 11032633 perft 6 416356876 id 392 -epd 1rnbbnkr/1pp1pppp/1q1p4/p7/4P3/5PN1/PPPP1BPP/QRNB2KR w HBhb - 1 9 +epd 1rnbbnkr/1pp1pppp/1q1p4/p7/4P3/5PN1/PPPP1BPP/QRNB2KR w HBhb - perft 1 26 perft 2 809 perft 3 21764 @@ -3539,7 +3539,7 @@ perft 5 20292750 perft 6 675408811 id 393 -epd qrnnbb1Q/ppp1pk1p/3p2p1/5p2/PP6/5P2/2PPP1PP/1RNNBBKR w HB - 0 9 +epd qrnnbb1Q/ppp1pk1p/3p2p1/5p2/PP6/5P2/2PPP1PP/1RNNBBKR w HB - perft 1 37 perft 2 751 perft 3 27902 @@ -3548,7 +3548,7 @@ perft 5 22443036 perft 6 515122176 id 394 -epd qrnnbkrb/p3p1pp/3p1p2/1pp5/PP2P3/8/2PP1PPP/QRNNBRKB w gb - 0 9 +epd qrnnbkrb/p3p1pp/3p1p2/1pp5/PP2P3/8/2PP1PPP/QRNNBRKB w gb - perft 1 30 perft 2 906 perft 3 27955 @@ -3557,7 +3557,7 @@ perft 5 27658191 perft 6 890966633 id 395 -epd qbrnnkbr/1p2pp1p/p1p3p1/3p4/6P1/P1N4P/1PPPPP2/QBR1NKBR w HChc - 0 9 +epd qbrnnkbr/1p2pp1p/p1p3p1/3p4/6P1/P1N4P/1PPPPP2/QBR1NKBR w HChc - perft 1 26 perft 2 701 perft 3 18930 @@ -3566,7 +3566,7 @@ perft 5 14733245 perft 6 416881799 id 396 -epd qr1b1kbr/1p1ppppp/1n1n4/p1p5/4P3/5NPP/PPPP1P2/QRNB1KBR w HBhb - 1 9 +epd qr1b1kbr/1p1ppppp/1n1n4/p1p5/4P3/5NPP/PPPP1P2/QRNB1KBR w HBhb - perft 1 26 perft 2 649 perft 3 17235 @@ -3575,7 +3575,7 @@ perft 5 12367604 perft 6 342165821 id 397 -epd qrnnkb1r/1pppppp1/7p/p4b2/4P3/5P1P/PPPP2PR/QRNNKBB1 w Bhb - 1 9 +epd qrnnkb1r/1pppppp1/7p/p4b2/4P3/5P1P/PPPP2PR/QRNNKBB1 w Bhb - perft 1 34 perft 2 941 perft 3 31720 @@ -3584,7 +3584,7 @@ perft 5 30307554 perft 6 888709821 id 398 -epd qr1nkrbb/p2ppppp/1pp5/8/3Pn3/1NP3P1/PP2PP1P/QR1NKRBB w FBfb - 1 9 +epd qr1nkrbb/p2ppppp/1pp5/8/3Pn3/1NP3P1/PP2PP1P/QR1NKRBB w FBfb - perft 1 19 perft 2 505 perft 3 11107 @@ -3593,7 +3593,7 @@ perft 5 7046501 perft 6 190414579 id 399 -epd bbrqn1kr/1pppp1pp/4n3/5p2/p5P1/3P4/PPP1PPKP/BBRQNN1R w hc - 0 9 +epd bbrqn1kr/1pppp1pp/4n3/5p2/p5P1/3P4/PPP1PPKP/BBRQNN1R w hc - perft 1 24 perft 2 573 perft 3 12963 @@ -3602,7 +3602,7 @@ perft 5 8191054 perft 6 227555387 id 400 -epd brqb1nkr/pppppp1p/8/4N1pn/5P2/6P1/PPPPP2P/BRQB1NKR w HBhb - 0 9 +epd brqb1nkr/pppppp1p/8/4N1pn/5P2/6P1/PPPPP2P/BRQB1NKR w HBhb - perft 1 26 perft 2 550 perft 3 14338 @@ -3611,7 +3611,7 @@ perft 5 8903754 perft 6 223437427 id 401 -epd brqnn1kr/pp3ppp/2pbp3/3p4/8/2NPP3/PPP1BPPP/BRQ1N1KR w HBhb - 0 9 +epd brqnn1kr/pp3ppp/2pbp3/3p4/8/2NPP3/PPP1BPPP/BRQ1N1KR w HBhb - perft 1 27 perft 2 780 perft 3 20760 @@ -3620,7 +3620,7 @@ perft 5 16243731 perft 6 463883447 id 402 -epd brq1nkrb/ppp2ppp/8/n2pp2P/P7/4P3/1PPP1PP1/BRQNNKRB w GBgb - 1 9 +epd brq1nkrb/ppp2ppp/8/n2pp2P/P7/4P3/1PPP1PP1/BRQNNKRB w GBgb - perft 1 17 perft 2 426 perft 3 8295 @@ -3629,7 +3629,7 @@ perft 5 5048497 perft 6 153986034 id 403 -epd rbbqn1kr/pp2p1pp/6n1/2pp1p2/2P4P/P7/BP1PPPP1/R1BQNNKR w HAha - 0 9 +epd rbbqn1kr/pp2p1pp/6n1/2pp1p2/2P4P/P7/BP1PPPP1/R1BQNNKR w HAha - perft 1 27 perft 2 916 perft 3 25798 @@ -3638,7 +3638,7 @@ perft 5 26302461 perft 6 924181432 id 404 -epd 1qbbn1kr/1ppppppp/r3n3/8/p1P5/P7/1P1PPPPP/RQBBNNKR w HAh - 1 9 +epd 1qbbn1kr/1ppppppp/r3n3/8/p1P5/P7/1P1PPPPP/RQBBNNKR w HAh - perft 1 29 perft 2 817 perft 3 24530 @@ -3647,7 +3647,7 @@ perft 5 22147642 perft 6 670707652 id 405 -epd rqbnnbkr/ppp1ppp1/7p/3p4/PP6/7P/1NPPPPP1/RQB1NBKR w HAa - 1 9 +epd rqbnnbkr/ppp1ppp1/7p/3p4/PP6/7P/1NPPPPP1/RQB1NBKR w HAa - perft 1 23 perft 2 572 perft 3 14509 @@ -3656,7 +3656,7 @@ perft 5 10416981 perft 6 288064942 id 406 -epd r1bnnkrb/q1ppp1pp/p7/1p3pB1/2P1P3/3P4/PP3PPP/RQ1NNKRB w GAga - 2 9 +epd r1bnnkrb/q1ppp1pp/p7/1p3pB1/2P1P3/3P4/PP3PPP/RQ1NNKRB w GAga - perft 1 31 perft 2 925 perft 3 27776 @@ -3665,7 +3665,7 @@ perft 5 26316355 perft 6 843078864 id 407 -epd rbqnb1kr/ppppp1pp/5p2/5N2/7P/1n3P2/PPPPP1P1/RBQNB1KR w HAha - 1 9 +epd rbqnb1kr/ppppp1pp/5p2/5N2/7P/1n3P2/PPPPP1P1/RBQNB1KR w HAha - perft 1 32 perft 2 864 perft 3 27633 @@ -3674,7 +3674,7 @@ perft 5 24738875 perft 6 707188107 id 408 -epd rqnbbn1r/ppppppp1/6k1/8/6Pp/2PN4/PP1PPPKP/RQ1BBN1R w - - 0 9 +epd rqnbbn1r/ppppppp1/6k1/8/6Pp/2PN4/PP1PPPKP/RQ1BBN1R w - - perft 1 27 perft 2 566 perft 3 15367 @@ -3683,7 +3683,7 @@ perft 5 9714509 perft 6 234622128 id 409 -epd rqnnbbkr/p1p2pp1/1p1p3p/4p3/4NP2/6P1/PPPPP2P/RQN1BBKR w HAha - 0 9 +epd rqnnbbkr/p1p2pp1/1p1p3p/4p3/4NP2/6P1/PPPPP2P/RQN1BBKR w HAha - perft 1 27 perft 2 631 perft 3 17923 @@ -3692,7 +3692,7 @@ perft 5 13307890 perft 6 356279813 id 410 -epd 1qnnbrkb/rppp1ppp/p3p3/8/4P3/2PP1P2/PP4PP/RQNNBKRB w GA - 1 9 +epd 1qnnbrkb/rppp1ppp/p3p3/8/4P3/2PP1P2/PP4PP/RQNNBKRB w GA - perft 1 24 perft 2 479 perft 3 12135 @@ -3701,7 +3701,7 @@ perft 5 7204345 perft 6 175460841 id 411 -epd rbqnn1br/p1pppk1p/1p4p1/5p2/8/P1P2P2/1PBPP1PP/R1QNNKBR w HA - 0 9 +epd rbqnn1br/p1pppk1p/1p4p1/5p2/8/P1P2P2/1PBPP1PP/R1QNNKBR w HA - perft 1 31 perft 2 756 perft 3 23877 @@ -3710,7 +3710,7 @@ perft 5 20036784 perft 6 554292502 id 412 -epd rqnbnkbr/1ppppp2/p5p1/8/1P4p1/4PP2/P1PP3P/RQNBNKBR w HAha - 0 9 +epd rqnbnkbr/1ppppp2/p5p1/8/1P4p1/4PP2/P1PP3P/RQNBNKBR w HAha - perft 1 24 perft 2 715 perft 3 18536 @@ -3719,7 +3719,7 @@ perft 5 16013189 perft 6 515078271 id 413 -epd rq1nkbbr/1p2pppp/p2n4/2pp4/1P4P1/P2N4/2PPPP1P/RQ1NKBBR w HAha - 1 9 +epd rq1nkbbr/1p2pppp/p2n4/2pp4/1P4P1/P2N4/2PPPP1P/RQ1NKBBR w HAha - perft 1 27 perft 2 694 perft 3 19840 @@ -3728,7 +3728,7 @@ perft 5 16685687 perft 6 494574415 id 414 -epd r1nnkrbb/pp1pppp1/2p3q1/7p/8/1PPP3P/P3PPP1/RQNNKRBB w FAfa - 1 9 +epd r1nnkrbb/pp1pppp1/2p3q1/7p/8/1PPP3P/P3PPP1/RQNNKRBB w FAfa - perft 1 18 perft 2 520 perft 3 10808 @@ -3737,7 +3737,7 @@ perft 5 7508201 perft 6 235103697 id 415 -epd bbrnqk1r/pppp3p/6p1/4pp2/3P2P1/8/PPP1PP1P/BBRN1NKR w HC - 0 9 +epd bbrnqk1r/pppp3p/6p1/4pp2/3P2P1/8/PPP1PP1P/BBRN1NKR w HC - perft 1 22 perft 2 566 perft 3 12965 @@ -3746,7 +3746,7 @@ perft 5 8721079 perft 6 259069471 id 416 -epd brnb1nkr/pppqpp2/3p2pp/8/3PP3/1P6/PBP2PPP/1RNBQNKR w HBhb - 0 9 +epd brnb1nkr/pppqpp2/3p2pp/8/3PP3/1P6/PBP2PPP/1RNBQNKR w HBhb - perft 1 32 perft 2 859 perft 3 28517 @@ -3755,7 +3755,7 @@ perft 5 27734108 perft 6 829785474 id 417 -epd brnq1b1r/ppp1ppkp/3p1np1/8/8/5P1P/PPPPPKPR/BRNQNB2 w - - 0 9 +epd brnq1b1r/ppp1ppkp/3p1np1/8/8/5P1P/PPPPPKPR/BRNQNB2 w - - perft 1 21 perft 2 511 perft 3 10951 @@ -3764,7 +3764,7 @@ perft 5 6372681 perft 6 167139732 id 418 -epd brnq1rkb/1pppppp1/3n3p/p7/8/P4NP1/1PPPPPRP/BRNQ1K1B w B - 0 9 +epd brnq1rkb/1pppppp1/3n3p/p7/8/P4NP1/1PPPPPRP/BRNQ1K1B w B - perft 1 25 perft 2 548 perft 3 14049 @@ -3773,7 +3773,7 @@ perft 5 9015901 perft 6 235249649 id 419 -epd rbb1qnkr/p1ppp1pp/1p3p2/6n1/8/1PN1P2P/P1PP1PP1/RBB1QNKR w HAha - 0 9 +epd rbb1qnkr/p1ppp1pp/1p3p2/6n1/8/1PN1P2P/P1PP1PP1/RBB1QNKR w HAha - perft 1 25 perft 2 673 perft 3 16412 @@ -3782,7 +3782,7 @@ perft 5 12099119 perft 6 361714466 id 420 -epd rnbb1nkr/1ppp1ppp/4p3/p5q1/6P1/1PP5/PB1PPP1P/RN1BQNKR w HAha - 1 9 +epd rnbb1nkr/1ppp1ppp/4p3/p5q1/6P1/1PP5/PB1PPP1P/RN1BQNKR w HAha - perft 1 19 perft 2 663 perft 3 14149 @@ -3791,7 +3791,7 @@ perft 5 11491355 perft 6 399135495 id 421 -epd rnbqnbkr/1pp1p2p/3p1p2/p5p1/5PP1/2P5/PPNPP2P/RNBQ1BKR w HAha - 0 9 +epd rnbqnbkr/1pp1p2p/3p1p2/p5p1/5PP1/2P5/PPNPP2P/RNBQ1BKR w HAha - perft 1 24 perft 2 647 perft 3 16679 @@ -3800,7 +3800,7 @@ perft 5 12649636 perft 6 361157611 id 422 -epd rnb2krb/pppqppnp/8/3p2p1/1P4P1/7P/P1PPPPB1/RNBQNKR1 w GAga - 1 9 +epd rnb2krb/pppqppnp/8/3p2p1/1P4P1/7P/P1PPPPB1/RNBQNKR1 w GAga - perft 1 24 perft 2 722 perft 3 18749 @@ -3809,7 +3809,7 @@ perft 5 16609220 perft 6 563558512 id 423 -epd rbnqb1kr/pppn1pp1/3p3p/4p3/1P6/P7/R1PPPPPP/1BNQBNKR w Hha - 1 9 +epd rbnqb1kr/pppn1pp1/3p3p/4p3/1P6/P7/R1PPPPPP/1BNQBNKR w Hha - perft 1 20 perft 2 538 perft 3 12277 @@ -3818,7 +3818,7 @@ perft 5 8687621 perft 6 255304141 id 424 -epd rnqb1nkr/p1pbp1pp/8/1pPp1p2/P2P4/8/1P2PPPP/RNQBBNKR w HAha - 1 9 +epd rnqb1nkr/p1pbp1pp/8/1pPp1p2/P2P4/8/1P2PPPP/RNQBBNKR w HAha - perft 1 35 perft 2 764 perft 3 26952 @@ -3827,7 +3827,7 @@ perft 5 22592380 perft 6 564255328 id 425 -epd rnq1bbkr/1p1ppp1p/4n3/p1p3p1/P1PP4/8/RP2PPPP/1NQNBBKR w Hha - 0 9 +epd rnq1bbkr/1p1ppp1p/4n3/p1p3p1/P1PP4/8/RP2PPPP/1NQNBBKR w Hha - perft 1 29 perft 2 709 perft 3 21296 @@ -3836,7 +3836,7 @@ perft 5 17597398 perft 6 506140370 id 426 -epd 1nqnbkrb/1pppp2p/r7/p4pp1/3P4/8/PPPBPPPP/RNQNK1RB w g - 0 9 +epd 1nqnbkrb/1pppp2p/r7/p4pp1/3P4/8/PPPBPPPP/RNQNK1RB w g - perft 1 27 perft 2 1028 perft 3 28534 @@ -3845,7 +3845,7 @@ perft 5 30251988 perft 6 1096869832 id 427 -epd rbnqnkbr/p1pp1p1p/8/1p2p3/3P2pP/2P5/PP2PPP1/RBNQNKBR w HAha - 0 9 +epd rbnqnkbr/p1pp1p1p/8/1p2p3/3P2pP/2P5/PP2PPP1/RBNQNKBR w HAha - perft 1 32 perft 2 832 perft 3 27120 @@ -3854,7 +3854,7 @@ perft 5 24945574 perft 6 724171581 id 428 -epd rnq1nkbr/1p1p1ppp/2p1pb2/p7/7P/2P5/PPNPPPPB/RNQB1K1R w HAha - 2 9 +epd rnq1nkbr/1p1p1ppp/2p1pb2/p7/7P/2P5/PPNPPPPB/RNQB1K1R w HAha - perft 1 31 perft 2 779 perft 3 24010 @@ -3863,7 +3863,7 @@ perft 5 19919434 perft 6 551494771 id 429 -epd rnqnk1br/p1ppp1bp/1p3p2/6p1/4N3/P5P1/1PPPPP1P/R1QNKBBR w HAha - 2 9 +epd rnqnk1br/p1ppp1bp/1p3p2/6p1/4N3/P5P1/1PPPPP1P/R1QNKBBR w HAha - perft 1 25 perft 2 717 perft 3 19396 @@ -3872,7 +3872,7 @@ perft 5 16525239 perft 6 507175842 id 430 -epd rnq1krbb/p1p1pppp/8/1p1p4/1n5B/2N2P2/PPPPP1PP/RNQ1KR1B w FAfa - 0 9 +epd rnq1krbb/p1p1pppp/8/1p1p4/1n5B/2N2P2/PPPPP1PP/RNQ1KR1B w FAfa - perft 1 28 perft 2 867 perft 3 24029 @@ -3881,7 +3881,7 @@ perft 5 21112751 perft 6 654808184 id 431 -epd bbrnnqkr/1pp1pppp/3p4/p7/P3P3/7P/1PPP1PP1/BBRNNQKR w HChc - 0 9 +epd bbrnnqkr/1pp1pppp/3p4/p7/P3P3/7P/1PPP1PP1/BBRNNQKR w HChc - perft 1 24 perft 2 405 perft 3 11025 @@ -3890,7 +3890,7 @@ perft 5 6196438 perft 6 131401224 id 432 -epd brnbnqkr/p1ppp3/1p5p/5Pp1/5P2/3N4/PPPPP2P/BRNB1QKR w HBhb g6 0 9 +epd brnbnqkr/p1ppp3/1p5p/5Pp1/5P2/3N4/PPPPP2P/BRNB1QKR w HBhb g6 perft 1 25 perft 2 785 perft 3 21402 @@ -3899,7 +3899,7 @@ perft 5 20687969 perft 6 695850727 id 433 -epd br1nqbkr/1ppppp2/pn6/6pp/2PP4/1N4P1/PP2PP1P/BR1NQBKR w HBhb - 0 9 +epd br1nqbkr/1ppppp2/pn6/6pp/2PP4/1N4P1/PP2PP1P/BR1NQBKR w HBhb - perft 1 25 perft 2 596 perft 3 16220 @@ -3908,7 +3908,7 @@ perft 5 12185361 perft 6 337805606 id 434 -epd 1rnnqkrb/p2ppp1p/1pp5/2N3p1/8/1P6/P1PPPPKP/BR1NQ1RB w gb - 0 9 +epd 1rnnqkrb/p2ppp1p/1pp5/2N3p1/8/1P6/P1PPPPKP/BR1NQ1RB w gb - perft 1 38 perft 2 960 perft 3 34831 @@ -3917,7 +3917,7 @@ perft 5 32490040 perft 6 880403591 id 435 -epd rbbnnqkr/pp3pp1/2p1p3/3p3p/3P3P/1PP5/P3PPP1/RBBNNQKR w HAha - 0 9 +epd rbbnnqkr/pp3pp1/2p1p3/3p3p/3P3P/1PP5/P3PPP1/RBBNNQKR w HAha - perft 1 30 perft 2 785 perft 3 23079 @@ -3926,7 +3926,7 @@ perft 5 19885037 perft 6 599219582 id 436 -epd rn1bnqkr/p1ppppp1/8/1p5p/P4P1P/3N4/1PPPP1b1/RNBB1QKR w HAha - 0 9 +epd rn1bnqkr/p1ppppp1/8/1p5p/P4P1P/3N4/1PPPP1b1/RNBB1QKR w HAha - perft 1 27 perft 2 752 perft 3 21735 @@ -3935,7 +3935,7 @@ perft 5 18862234 perft 6 547415271 id 437 -epd 1nbnqbkr/1p1p1ppp/r3p3/p1p5/P3P3/3Q4/1PPP1PPP/RNBN1BKR w HAh - 2 9 +epd 1nbnqbkr/1p1p1ppp/r3p3/p1p5/P3P3/3Q4/1PPP1PPP/RNBN1BKR w HAh - perft 1 33 perft 2 721 perft 3 24278 @@ -3944,7 +3944,7 @@ perft 5 19648535 perft 6 496023732 id 438 -epd rnbnqkrb/2pppppp/1p6/p7/1PP5/4N2P/P2PPPP1/RNB1QKRB w GAg - 0 9 +epd rnbnqkrb/2pppppp/1p6/p7/1PP5/4N2P/P2PPPP1/RNB1QKRB w GAg - perft 1 23 perft 2 570 perft 3 14225 @@ -3953,7 +3953,7 @@ perft 5 10022614 perft 6 279545007 id 439 -epd rbnnbq1r/ppppppkp/6p1/N7/4P3/P7/1PPP1PPP/RB1NBQKR w HA - 5 9 +epd rbnnbq1r/ppppppkp/6p1/N7/4P3/P7/1PPP1PPP/RB1NBQKR w HA - perft 1 27 perft 2 620 perft 3 18371 @@ -3962,7 +3962,7 @@ perft 5 13909432 perft 6 349478320 id 440 -epd r1nbbqkr/pppppp1p/8/8/1n3Pp1/3N1QP1/PPPPP2P/RN1BB1KR w HAha - 0 9 +epd r1nbbqkr/pppppp1p/8/8/1n3Pp1/3N1QP1/PPPPP2P/RN1BB1KR w HAha - perft 1 31 perft 2 791 perft 3 25431 @@ -3971,7 +3971,7 @@ perft 5 22408813 perft 6 636779732 id 441 -epd rnq1bbkr/pp1p1ppp/2pnp3/8/7P/1QP5/PP1PPPPR/RNN1BBK1 w Aha - 2 9 +epd rnq1bbkr/pp1p1ppp/2pnp3/8/7P/1QP5/PP1PPPPR/RNN1BBK1 w Aha - perft 1 28 perft 2 559 perft 3 16838 @@ -3980,7 +3980,7 @@ perft 5 12242780 perft 6 315431511 id 442 -epd rnnqbrkb/2ppppp1/1p1N4/p6p/4P3/8/PPPP1PPP/R1NQBKRB w GA - 0 9 +epd rnnqbrkb/2ppppp1/1p1N4/p6p/4P3/8/PPPP1PPP/R1NQBKRB w GA - perft 1 32 perft 2 638 perft 3 20591 @@ -3989,7 +3989,7 @@ perft 5 14395828 perft 6 331782223 id 443 -epd rbnnq1br/pppp1kp1/4pp2/7p/PP6/2PP4/4PPPP/RBNNQKBR w HA - 0 9 +epd rbnnq1br/pppp1kp1/4pp2/7p/PP6/2PP4/4PPPP/RBNNQKBR w HA - perft 1 21 perft 2 521 perft 3 12201 @@ -3998,7 +3998,7 @@ perft 5 8239159 perft 6 227346638 id 444 -epd rnnbqkbr/p2ppp2/7p/1pp3p1/2P2N2/8/PP1PPPPP/RN1BQKBR w HAha - 0 9 +epd rnnbqkbr/p2ppp2/7p/1pp3p1/2P2N2/8/PP1PPPPP/RN1BQKBR w HAha - perft 1 25 perft 2 528 perft 3 13896 @@ -4007,7 +4007,7 @@ perft 5 9079829 perft 6 232750602 id 445 -epd rnn1kbbr/ppppqp2/6p1/2N1p2p/P7/2P5/1P1PPPPP/RN1QKBBR w HAha - 2 9 +epd rnn1kbbr/ppppqp2/6p1/2N1p2p/P7/2P5/1P1PPPPP/RN1QKBBR w HAha - perft 1 27 perft 2 801 perft 3 22088 @@ -4016,7 +4016,7 @@ perft 5 20334071 perft 6 682580976 id 446 -epd rnnqkrbb/p1p1p1pp/1p3p2/8/3p2Q1/P1P1P3/1P1P1PPP/RNN1KRBB w FAfa - 0 9 +epd rnnqkrbb/p1p1p1pp/1p3p2/8/3p2Q1/P1P1P3/1P1P1PPP/RNN1KRBB w FAfa - perft 1 37 perft 2 1014 perft 3 34735 @@ -4025,7 +4025,7 @@ perft 5 32921537 perft 6 988770109 id 447 -epd bbrnk1qr/1pppppp1/p4n1p/8/P2P2N1/8/1PP1PPPP/BBR1NKQR w HC - 1 9 +epd bbrnk1qr/1pppppp1/p4n1p/8/P2P2N1/8/1PP1PPPP/BBR1NKQR w HC - perft 1 21 perft 2 481 perft 3 11213 @@ -4034,7 +4034,7 @@ perft 5 7015419 perft 6 187564853 id 448 -epd brnbnkqr/1pp1p1p1/p2p1p2/7p/1P4PP/8/PBPPPP2/1RNBNKQR w HBhb - 0 9 +epd brnbnkqr/1pp1p1p1/p2p1p2/7p/1P4PP/8/PBPPPP2/1RNBNKQR w HBhb - perft 1 31 perft 2 743 perft 3 24260 @@ -4043,7 +4043,7 @@ perft 5 22391185 perft 6 653721389 id 449 -epd br2kbqr/ppppp1pp/3n1p2/3P4/3n3P/3N4/PPP1PPP1/BR1NKBQR w HBhb - 3 9 +epd br2kbqr/ppppp1pp/3n1p2/3P4/3n3P/3N4/PPP1PPP1/BR1NKBQR w HBhb - perft 1 25 perft 2 872 perft 3 22039 @@ -4052,7 +4052,7 @@ perft 5 20281962 perft 6 685749952 id 450 -epd br1nkqrb/ppppppp1/8/7p/4P3/n1P2PP1/PP1P3P/BRNNKQRB w GBgb - 0 9 +epd br1nkqrb/ppppppp1/8/7p/4P3/n1P2PP1/PP1P3P/BRNNKQRB w GBgb - perft 1 28 perft 2 607 perft 3 16934 @@ -4061,7 +4061,7 @@ perft 5 11607818 perft 6 294181806 id 451 -epd rbbn1kqr/pp1pp1p1/2pn3p/5p2/5P2/1P1N4/PNPPP1PP/RBB2KQR w HAha - 1 9 +epd rbbn1kqr/pp1pp1p1/2pn3p/5p2/5P2/1P1N4/PNPPP1PP/RBB2KQR w HAha - perft 1 27 perft 2 725 perft 3 21543 @@ -4070,7 +4070,7 @@ perft 5 19239812 perft 6 581716972 id 452 -epd rnbbnk1r/pp1ppp1p/6q1/2p5/PP4p1/4P3/2PP1PPP/RNBBNKQR w HAha - 1 9 +epd rnbbnk1r/pp1ppp1p/6q1/2p5/PP4p1/4P3/2PP1PPP/RNBBNKQR w HAha - perft 1 25 perft 2 1072 perft 3 26898 @@ -4079,7 +4079,7 @@ perft 5 28469879 perft 6 1122703887 id 453 -epd rnbnkbqr/1pp3pp/3p4/p3pp2/3P2P1/2N1N3/PPP1PP1P/R1B1KBQR w HAha - 0 9 +epd rnbnkbqr/1pp3pp/3p4/p3pp2/3P2P1/2N1N3/PPP1PP1P/R1B1KBQR w HAha - perft 1 31 perft 2 1028 perft 3 32907 @@ -4088,7 +4088,7 @@ perft 5 36025223 perft 6 1211187800 id 454 -epd r1bnkqrb/1ppppppp/p3n3/8/6P1/4N3/PPPPPPRP/RNB1KQ1B w Aga - 1 9 +epd r1bnkqrb/1ppppppp/p3n3/8/6P1/4N3/PPPPPPRP/RNB1KQ1B w Aga - perft 1 23 perft 2 457 perft 3 11416 @@ -4097,7 +4097,7 @@ perft 5 6666787 perft 6 159759052 id 455 -epd rbn1bkqr/p1pp1pp1/1pn5/4p2p/7P/1PBP4/P1P1PPP1/RBNN1KQR w HAha - 0 9 +epd rbn1bkqr/p1pp1pp1/1pn5/4p2p/7P/1PBP4/P1P1PPP1/RBNN1KQR w HAha - perft 1 23 perft 2 470 perft 3 11649 @@ -4106,7 +4106,7 @@ perft 5 6963287 perft 6 172833738 id 456 -epd rnnbbkqr/3ppppp/p7/1pp5/P6P/6P1/1PPPPP2/RNNBBKQR w HAha - 0 9 +epd rnnbbkqr/3ppppp/p7/1pp5/P6P/6P1/1PPPPP2/RNNBBKQR w HAha - perft 1 26 perft 2 569 perft 3 15733 @@ -4115,7 +4115,7 @@ perft 5 11008114 perft 6 284485303 id 457 -epd r1nk1bqr/1pppp1pp/2n5/p4p1b/5P2/1N4B1/PPPPP1PP/RN1K1BQR w HAha - 2 9 +epd r1nk1bqr/1pppp1pp/2n5/p4p1b/5P2/1N4B1/PPPPP1PP/RN1K1BQR w HAha - perft 1 25 perft 2 824 perft 3 21983 @@ -4124,7 +4124,7 @@ perft 5 20904119 perft 6 716170771 id 458 -epd r1nkbqrb/p2pppp1/npp4p/8/4PP2/2N4P/PPPP2P1/R1NKBQRB w GAga - 0 9 +epd r1nkbqrb/p2pppp1/npp4p/8/4PP2/2N4P/PPPP2P1/R1NKBQRB w GAga - perft 1 31 perft 2 548 perft 3 17480 @@ -4133,7 +4133,7 @@ perft 5 11469548 perft 6 255067638 id 459 -epd rbnnkqbr/ppppp2p/5p2/6p1/2P1B3/P6P/1P1PPPP1/R1NNKQBR w HAha - 1 9 +epd rbnnkqbr/ppppp2p/5p2/6p1/2P1B3/P6P/1P1PPPP1/R1NNKQBR w HAha - perft 1 31 perft 2 809 perft 3 24956 @@ -4142,7 +4142,7 @@ perft 5 21247414 perft 6 606221516 id 460 -epd 1r1bkqbr/pppp1ppp/2nnp3/8/2P5/N4P2/PP1PP1PP/1RNBKQBR w Hh - 0 9 +epd 1r1bkqbr/pppp1ppp/2nnp3/8/2P5/N4P2/PP1PP1PP/1RNBKQBR w Hh - perft 1 28 perft 2 810 perft 3 22844 @@ -4151,7 +4151,7 @@ perft 5 20188622 perft 6 636748147 id 461 -epd rn1kqbbr/p1pppp1p/1p4p1/1n6/1P2P3/4Q2P/P1PP1PP1/RNNK1BBR w HAha - 1 9 +epd rn1kqbbr/p1pppp1p/1p4p1/1n6/1P2P3/4Q2P/P1PP1PP1/RNNK1BBR w HAha - perft 1 39 perft 2 848 perft 3 30100 @@ -4160,7 +4160,7 @@ perft 5 25594662 perft 6 659615710 id 462 -epd rn1kqrbb/pppppppp/8/8/2nP2P1/1P2P3/P1P2P1P/RNNKQRBB w FAfa - 1 9 +epd rn1kqrbb/pppppppp/8/8/2nP2P1/1P2P3/P1P2P1P/RNNKQRBB w FAfa - perft 1 29 perft 2 766 perft 3 21701 @@ -4169,7 +4169,7 @@ perft 5 16944425 perft 6 456898648 id 463 -epd b1rnnkrq/bpppppp1/7p/8/1p6/2B5/PNPPPPPP/1BR1NKRQ w GCgc - 2 9 +epd b1rnnkrq/bpppppp1/7p/8/1p6/2B5/PNPPPPPP/1BR1NKRQ w GCgc - perft 1 25 perft 2 667 perft 3 17253 @@ -4178,7 +4178,7 @@ perft 5 12865247 perft 6 365621294 id 464 -epd brnb1krq/pppppppp/8/5P2/2P1n2P/8/PP1PP1P1/BRNBNKRQ w GBgb - 1 9 +epd brnb1krq/pppppppp/8/5P2/2P1n2P/8/PP1PP1P1/BRNBNKRQ w GBgb - perft 1 23 perft 2 620 perft 3 14882 @@ -4187,7 +4187,7 @@ perft 5 10776855 perft 6 300125003 id 465 -epd b1nnkbrq/pr1pppp1/1p5p/2p5/P2N1P2/8/1PPPP1PP/BR1NKBRQ w GBg - 0 9 +epd b1nnkbrq/pr1pppp1/1p5p/2p5/P2N1P2/8/1PPPP1PP/BR1NKBRQ w GBg - perft 1 24 perft 2 472 perft 3 12181 @@ -4196,7 +4196,7 @@ perft 5 7370758 perft 6 178605165 id 466 -epd br1nkrqb/p1p1p1pp/3n4/1p1p1p2/5N1P/4P3/PPPP1PP1/BR1NKRQB w FBfb - 0 9 +epd br1nkrqb/p1p1p1pp/3n4/1p1p1p2/5N1P/4P3/PPPP1PP1/BR1NKRQB w FBfb - perft 1 24 perft 2 775 perft 3 19398 @@ -4205,7 +4205,7 @@ perft 5 16429837 perft 6 539767605 id 467 -epd rbbnnkrq/p2pp1pp/2p5/5p2/1pPP1B2/P7/1P2PPPP/RB1NNKRQ w GAga - 0 9 +epd rbbnnkrq/p2pp1pp/2p5/5p2/1pPP1B2/P7/1P2PPPP/RB1NNKRQ w GAga - perft 1 34 perft 2 921 perft 3 30474 @@ -4214,7 +4214,7 @@ perft 5 28095833 perft 6 806446436 id 468 -epd rnbbnkr1/1p1ppp1p/2p3p1/p7/2Pq4/1P1P4/P2BPPPP/RN1BNKRQ w GAga - 2 9 +epd rnbbnkr1/1p1ppp1p/2p3p1/p7/2Pq4/1P1P4/P2BPPPP/RN1BNKRQ w GAga - perft 1 26 perft 2 1139 perft 3 29847 @@ -4223,7 +4223,7 @@ perft 5 32825932 perft 6 1281760240 id 469 -epd 1rbnkbrq/pppppp2/n5pp/2P5/P7/4N3/1P1PPPPP/RNB1KBRQ w GAg - 2 9 +epd 1rbnkbrq/pppppp2/n5pp/2P5/P7/4N3/1P1PPPPP/RNB1KBRQ w GAg - perft 1 23 perft 2 574 perft 3 14146 @@ -4232,7 +4232,7 @@ perft 5 10203438 perft 6 301874034 id 470 -epd 1nbnkr1b/rppppppq/p7/7p/1P5P/3P2P1/P1P1PP2/RNBNKRQB w FAf - 1 9 +epd 1nbnkr1b/rppppppq/p7/7p/1P5P/3P2P1/P1P1PP2/RNBNKRQB w FAf - perft 1 33 perft 2 823 perft 3 26696 @@ -4241,7 +4241,7 @@ perft 5 23266182 perft 6 672294132 id 471 -epd rbn1bkrq/ppppp3/4n2p/5pp1/1PN5/2P5/P2PPPPP/RBN1BKRQ w GAga - 0 9 +epd rbn1bkrq/ppppp3/4n2p/5pp1/1PN5/2P5/P2PPPPP/RBN1BKRQ w GAga - perft 1 27 perft 2 859 perft 3 24090 @@ -4250,7 +4250,7 @@ perft 5 23075785 perft 6 789152120 id 472 -epd r1nbbkrq/1ppp2pp/2n2p2/p3p3/5P2/1N4BP/PPPPP1P1/RN1B1KRQ w GAga - 0 9 +epd r1nbbkrq/1ppp2pp/2n2p2/p3p3/5P2/1N4BP/PPPPP1P1/RN1B1KRQ w GAga - perft 1 25 perft 2 774 perft 3 20141 @@ -4259,7 +4259,7 @@ perft 5 16718577 perft 6 515864053 id 473 -epd rnnkbbrq/1pppp1p1/5p2/7p/p6P/3N1P2/PPPPP1PQ/RN1KBBR1 w GAga - 0 9 +epd rnnkbbrq/1pppp1p1/5p2/7p/p6P/3N1P2/PPPPP1PQ/RN1KBBR1 w GAga - perft 1 29 perft 2 673 perft 3 20098 @@ -4268,7 +4268,7 @@ perft 5 15545590 perft 6 416359581 id 474 -epd r1nkbrqb/pppp1p2/n3p1p1/7p/2P2P2/1P6/P2PPQPP/RNNKBR1B w FAfa - 0 9 +epd r1nkbrqb/pppp1p2/n3p1p1/7p/2P2P2/1P6/P2PPQPP/RNNKBR1B w FAfa - perft 1 27 perft 2 722 perft 3 21397 @@ -4277,7 +4277,7 @@ perft 5 18742426 perft 6 537750982 id 475 -epd rbnnkr1q/1ppp2pp/p4p2/P2bp3/4P2P/8/1PPP1PP1/RBNNKRBQ w FAfa - 1 9 +epd rbnnkr1q/1ppp2pp/p4p2/P2bp3/4P2P/8/1PPP1PP1/RBNNKRBQ w FAfa - perft 1 26 perft 2 848 perft 3 23387 @@ -4286,7 +4286,7 @@ perft 5 21591790 perft 6 675163653 id 476 -epd rn1bkrb1/1ppppp1p/pn4p1/8/P2q3P/3P4/NPP1PPP1/RN1BKRBQ w FAfa - 1 9 +epd rn1bkrb1/1ppppp1p/pn4p1/8/P2q3P/3P4/NPP1PPP1/RN1BKRBQ w FAfa - perft 1 22 perft 2 803 perft 3 18322 @@ -4295,7 +4295,7 @@ perft 5 15847763 perft 6 536419559 id 477 -epd rn1krbbq/pppp1npp/4pp2/8/4P2P/3P2P1/PPP2P2/RNNKRBBQ w EAea - 1 9 +epd rn1krbbq/pppp1npp/4pp2/8/4P2P/3P2P1/PPP2P2/RNNKRBBQ w EAea - perft 1 29 perft 2 810 perft 3 23968 @@ -4304,7 +4304,7 @@ perft 5 20361517 perft 6 575069358 id 478 -epd rnn1rqbb/ppkp1pp1/2p1p2p/2P5/8/3P1P2/PP2P1PP/RNNKRQBB w EA - 0 9 +epd rnn1rqbb/ppkp1pp1/2p1p2p/2P5/8/3P1P2/PP2P1PP/RNNKRQBB w EA - perft 1 22 perft 2 506 perft 3 11973 @@ -4313,7 +4313,7 @@ perft 5 7287368 perft 6 189865944 id 479 -epd bbqr1knr/pppppp1p/8/4n1p1/2P1P3/6P1/PPQP1P1P/BB1RNKNR w HDhd - 0 9 +epd bbqr1knr/pppppp1p/8/4n1p1/2P1P3/6P1/PPQP1P1P/BB1RNKNR w HDhd - perft 1 26 perft 2 650 perft 3 18253 @@ -4322,7 +4322,7 @@ perft 5 14301029 perft 6 394943978 id 480 -epd bq1bnknr/pprppp1p/8/2p3p1/4PPP1/8/PPPP3P/BQRBNKNR w HCh - 0 9 +epd bq1bnknr/pprppp1p/8/2p3p1/4PPP1/8/PPPP3P/BQRBNKNR w HCh - perft 1 24 perft 2 548 perft 3 14021 @@ -4331,7 +4331,7 @@ perft 5 9374021 perft 6 250988458 id 481 -epd bqrnkb1r/1p2pppp/p1pp3n/5Q2/2P4P/5N2/PP1PPPP1/B1RNKB1R w HChc - 0 9 +epd bqrnkb1r/1p2pppp/p1pp3n/5Q2/2P4P/5N2/PP1PPPP1/B1RNKB1R w HChc - perft 1 46 perft 2 823 perft 3 33347 @@ -4340,7 +4340,7 @@ perft 5 26130444 perft 6 582880996 id 482 -epd bq1rknrb/pppppp1p/4n3/6p1/4P1P1/3P1P2/PPP4P/BQRNKNRB w GCg - 0 9 +epd bq1rknrb/pppppp1p/4n3/6p1/4P1P1/3P1P2/PPP4P/BQRNKNRB w GCg - perft 1 23 perft 2 618 perft 3 14815 @@ -4349,7 +4349,7 @@ perft 5 10606831 perft 6 315124518 id 483 -epd q1brnknr/pp1pp1p1/8/2p2p1p/5b2/P4N2/1PPPP1PP/QBBRK1NR w hd - 0 9 +epd q1brnknr/pp1pp1p1/8/2p2p1p/5b2/P4N2/1PPPP1PP/QBBRK1NR w hd - perft 1 22 perft 2 675 perft 3 15778 @@ -4358,7 +4358,7 @@ perft 5 12077228 perft 6 368479752 id 484 -epd qrbbnknr/1p1ppp1p/p1p5/8/1P2P1p1/3P1B2/P1P2PPP/QRB1NKNR w HBhb - 0 9 +epd qrbbnknr/1p1ppp1p/p1p5/8/1P2P1p1/3P1B2/P1P2PPP/QRB1NKNR w HBhb - perft 1 32 perft 2 722 perft 3 24049 @@ -4367,7 +4367,7 @@ perft 5 19584539 perft 6 484814878 id 485 -epd qrb1kbnr/p3pppp/2n5/1ppp4/7P/3P1P2/PPP1P1PR/QRBNKBN1 w Bhb - 0 9 +epd qrb1kbnr/p3pppp/2n5/1ppp4/7P/3P1P2/PPP1P1PR/QRBNKBN1 w Bhb - perft 1 26 perft 2 831 perft 3 22606 @@ -4376,7 +4376,7 @@ perft 5 20500804 perft 6 662608969 id 486 -epd qrbnknrb/ppp1pp2/6p1/7p/PPNp4/8/2PPPPPP/QRB1KNRB w GBgb - 0 9 +epd qrbnknrb/ppp1pp2/6p1/7p/PPNp4/8/2PPPPPP/QRB1KNRB w GBgb - perft 1 31 perft 2 840 perft 3 26762 @@ -4385,7 +4385,7 @@ perft 5 24422614 perft 6 701363800 id 487 -epd qbrnbknr/pp1pp1pp/8/2p2p2/3Q4/PP6/2PPPPPP/1BRNBKNR w HChc - 0 9 +epd qbrnbknr/pp1pp1pp/8/2p2p2/3Q4/PP6/2PPPPPP/1BRNBKNR w HChc - perft 1 38 perft 2 1121 perft 3 39472 @@ -4394,7 +4394,7 @@ perft 5 41108769 perft 6 1285503872 id 488 -epd qr1bbk1r/pppppp1p/1n6/5np1/4B3/1PP5/P2PPPPP/QRN1BKNR w HBhb - 0 9 +epd qr1bbk1r/pppppp1p/1n6/5np1/4B3/1PP5/P2PPPPP/QRN1BKNR w HBhb - perft 1 25 perft 2 694 perft 3 16938 @@ -4403,7 +4403,7 @@ perft 5 12164609 perft 6 345122090 id 489 -epd qrnkbbnr/1p1pp2p/p7/2p1Npp1/6P1/7P/PPPPPP2/QR1KBBNR w HBhb - 0 9 +epd qrnkbbnr/1p1pp2p/p7/2p1Npp1/6P1/7P/PPPPPP2/QR1KBBNR w HBhb - perft 1 27 perft 2 586 perft 3 16348 @@ -4412,7 +4412,7 @@ perft 5 11409633 perft 6 298054792 id 490 -epd qrnkbnrb/pp1p1p2/2p1p1pp/4N3/P4P2/8/1PPPP1PP/QR1KBNRB w GBgb - 0 9 +epd qrnkbnrb/pp1p1p2/2p1p1pp/4N3/P4P2/8/1PPPP1PP/QR1KBNRB w GBgb - perft 1 32 perft 2 645 perft 3 20737 @@ -4421,7 +4421,7 @@ perft 5 15037464 perft 6 358531599 id 491 -epd qbrnknbr/1pppppp1/p6p/8/1P6/3PP3/PQP2PPP/1BRNKNBR w HChc - 3 9 +epd qbrnknbr/1pppppp1/p6p/8/1P6/3PP3/PQP2PPP/1BRNKNBR w HChc - perft 1 26 perft 2 595 perft 3 16755 @@ -4430,7 +4430,7 @@ perft 5 12214768 perft 6 323518628 id 492 -epd qrnbk1br/1ppppp1p/p5p1/8/4Pn2/4K1P1/PPPP1P1P/QRNB1NBR w hb - 0 9 +epd qrnbk1br/1ppppp1p/p5p1/8/4Pn2/4K1P1/PPPP1P1P/QRNB1NBR w hb - perft 1 24 perft 2 609 perft 3 13776 @@ -4439,7 +4439,7 @@ perft 5 8538539 perft 6 230364479 id 493 -epd qrnk1bbr/1pnp1ppp/p1p1p3/8/3Q4/1P1N3P/P1PPPPP1/1RNK1BBR w HBhb - 0 9 +epd qrnk1bbr/1pnp1ppp/p1p1p3/8/3Q4/1P1N3P/P1PPPPP1/1RNK1BBR w HBhb - perft 1 43 perft 2 1106 perft 3 42898 @@ -4448,7 +4448,7 @@ perft 5 41695761 perft 6 1113836402 id 494 -epd qrnknrb1/pppppp2/8/6pp/4P2P/3P1P2/PbP3P1/QRNKNRBB w FBfb - 0 9 +epd qrnknrb1/pppppp2/8/6pp/4P2P/3P1P2/PbP3P1/QRNKNRBB w FBfb - perft 1 24 perft 2 658 perft 3 17965 @@ -4457,7 +4457,7 @@ perft 5 14457245 perft 6 400971226 id 495 -epd bbrqnrk1/ppp2ppp/7n/3pp3/8/P4N1N/1PPPPPPP/BBRQ1RK1 w - - 1 9 +epd bbrqnrk1/ppp2ppp/7n/3pp3/8/P4N1N/1PPPPPPP/BBRQ1RK1 w - - perft 1 22 perft 2 503 perft 3 12078 @@ -4466,7 +4466,7 @@ perft 5 8080951 perft 6 224960353 id 496 -epd brqbnk1r/1ppp1ppp/8/p3pn2/8/2PP1P2/PP2PKPP/BRQBN1NR w hb - 1 9 +epd brqbnk1r/1ppp1ppp/8/p3pn2/8/2PP1P2/PP2PKPP/BRQBN1NR w hb - perft 1 25 perft 2 745 perft 3 19387 @@ -4475,7 +4475,7 @@ perft 5 15520298 perft 6 460840861 id 497 -epd brqnkbnr/pp2pp1p/3p4/2p5/5p2/3P3P/PPP1PPP1/B1RNKBNR w Hhb - 0 9 +epd brqnkbnr/pp2pp1p/3p4/2p5/5p2/3P3P/PPP1PPP1/B1RNKBNR w Hhb - perft 1 19 perft 2 516 perft 3 10755 @@ -4484,7 +4484,7 @@ perft 5 6995034 perft 6 214340699 id 498 -epd brq1kn1b/1ppppprp/2n3p1/p7/P1N5/6P1/1PPPPP1P/BRQNK1RB w GBb - 2 9 +epd brq1kn1b/1ppppprp/2n3p1/p7/P1N5/6P1/1PPPPP1P/BRQNK1RB w GBb - perft 1 29 perft 2 557 perft 3 16739 @@ -4493,7 +4493,7 @@ perft 5 10840256 perft 6 249999654 id 499 -epd rbbq1k1r/ppp1pppp/7n/1n1p4/5P2/P2P4/1PPBP1PP/RB1QNKNR w HAha - 1 9 +epd rbbq1k1r/ppp1pppp/7n/1n1p4/5P2/P2P4/1PPBP1PP/RB1QNKNR w HAha - perft 1 25 perft 2 769 perft 3 20110 @@ -4502,7 +4502,7 @@ perft 5 17438715 perft 6 570893953 id 500 -epd r1bbnk1r/qpp1pppp/p6n/3p4/1P6/5N1P/P1PPPPP1/RQBBK1NR w ha - 0 9 +epd r1bbnk1r/qpp1pppp/p6n/3p4/1P6/5N1P/P1PPPPP1/RQBBK1NR w ha - perft 1 23 perft 2 728 perft 3 18209 @@ -4511,7 +4511,7 @@ perft 5 16053564 perft 6 529082811 id 501 -epd rqbnkbnr/1pp2p1p/3p4/p3p1p1/8/2P2P2/PP1PPNPP/RQBNKB1R w HAha - 0 9 +epd rqbnkbnr/1pp2p1p/3p4/p3p1p1/8/2P2P2/PP1PPNPP/RQBNKB1R w HAha - perft 1 26 perft 2 772 perft 3 21903 @@ -4520,7 +4520,7 @@ perft 5 19571559 perft 6 593915677 id 502 -epd r1bnknrb/pqppp1p1/1p5p/5p2/7P/3P2N1/PPP1PPP1/RQBNK1RB w GAga - 2 9 +epd r1bnknrb/pqppp1p1/1p5p/5p2/7P/3P2N1/PPP1PPP1/RQBNK1RB w GAga - perft 1 27 perft 2 748 perft 3 20291 @@ -4529,7 +4529,7 @@ perft 5 16324542 perft 6 506453626 id 503 -epd rbqnbknr/pp1pppp1/8/2p5/3P3p/5N1P/PPP1PPPR/RBQNBK2 w Aha - 0 9 +epd rbqnbknr/pp1pppp1/8/2p5/3P3p/5N1P/PPP1PPPR/RBQNBK2 w Aha - perft 1 30 perft 2 859 perft 3 26785 @@ -4538,7 +4538,7 @@ perft 5 26363334 perft 6 842796987 id 504 -epd rqnbbrk1/ppppppp1/8/5n1p/3P3P/2B3P1/PPP1PP2/RQNB1KNR w HA - 0 9 +epd rqnbbrk1/ppppppp1/8/5n1p/3P3P/2B3P1/PPP1PP2/RQNB1KNR w HA - perft 1 22 perft 2 505 perft 3 11452 @@ -4547,7 +4547,7 @@ perft 5 7055215 perft 6 186760784 id 505 -epd rqnkbbnr/pp2p1p1/8/2pp1p1p/3PPP2/8/PPP1N1PP/RQNKBB1R w HAha - 0 9 +epd rqnkbbnr/pp2p1p1/8/2pp1p1p/3PPP2/8/PPP1N1PP/RQNKBB1R w HAha - perft 1 28 perft 2 832 perft 3 23142 @@ -4556,7 +4556,7 @@ perft 5 20429246 perft 6 663183060 id 506 -epd rqnkbnr1/pppp2bp/6p1/4pp2/1P2P3/3NN3/P1PP1PPP/RQ1KB1RB w GAga - 0 9 +epd rqnkbnr1/pppp2bp/6p1/4pp2/1P2P3/3NN3/P1PP1PPP/RQ1KB1RB w GAga - perft 1 28 perft 2 641 perft 3 18835 @@ -4565,7 +4565,7 @@ perft 5 14038570 perft 6 364210162 id 507 -epd rbq2kbr/pppppppp/2n5/P7/3P1n2/2P5/1P2PPPP/RBQNKNBR w HA - 1 9 +epd rbq2kbr/pppppppp/2n5/P7/3P1n2/2P5/1P2PPPP/RBQNKNBR w HA - perft 1 31 perft 2 889 perft 3 27028 @@ -4574,7 +4574,7 @@ perft 5 24299415 perft 6 692180754 id 508 -epd rq1bkn1r/ppppp2p/3n4/5pp1/2b3P1/1N1P1P2/PPP1P2P/RQ1BKNBR w HAha - 1 9 +epd rq1bkn1r/ppppp2p/3n4/5pp1/2b3P1/1N1P1P2/PPP1P2P/RQ1BKNBR w HAha - perft 1 28 perft 2 810 perft 3 22667 @@ -4583,7 +4583,7 @@ perft 5 18719949 perft 6 556282676 id 509 -epd r1nknbbr/p2ppp1p/1pp3p1/8/1P6/4P3/P1PPNPPq/R1QKNBBR w HAha - 0 9 +epd r1nknbbr/p2ppp1p/1pp3p1/8/1P6/4P3/P1PPNPPq/R1QKNBBR w HAha - perft 1 24 perft 2 797 perft 3 22144 @@ -4592,7 +4592,7 @@ perft 5 21862776 perft 6 716521139 id 510 -epd rqnknrbb/ppp1p3/5ppp/2Np4/2P5/4P3/PP1P1PPP/RQNK1RBB w FAfa - 0 9 +epd rqnknrbb/ppp1p3/5ppp/2Np4/2P5/4P3/PP1P1PPP/RQNK1RBB w FAfa - perft 1 34 perft 2 686 perft 3 23277 @@ -4601,7 +4601,7 @@ perft 5 17664543 perft 6 423574794 id 511 -epd 1brnqknr/2p1pppp/p2p4/1P6/6P1/4Nb2/PP1PPP1P/BBR1QKNR w HChc - 1 9 +epd 1brnqknr/2p1pppp/p2p4/1P6/6P1/4Nb2/PP1PPP1P/BBR1QKNR w HChc - perft 1 34 perft 2 1019 perft 3 32982 @@ -4610,7 +4610,7 @@ perft 5 33322477 perft 6 1043293394 id 512 -epd brn1qknr/1p1pppp1/pb5p/Q1p5/3P3P/8/PPP1PPPR/BRNB1KN1 w Bhb - 2 9 +epd brn1qknr/1p1pppp1/pb5p/Q1p5/3P3P/8/PPP1PPPR/BRNB1KN1 w Bhb - perft 1 32 perft 2 642 perft 3 20952 @@ -4619,7 +4619,7 @@ perft 5 15454749 perft 6 371861782 id 513 -epd brnqkbnr/pppppp2/8/6pp/6P1/P2P1P2/1PP1P2P/BRNQKBNR w HBhb - 0 9 +epd brnqkbnr/pppppp2/8/6pp/6P1/P2P1P2/1PP1P2P/BRNQKBNR w HBhb - perft 1 20 perft 2 441 perft 3 9782 @@ -4628,7 +4628,7 @@ perft 5 5770284 perft 6 153051835 id 514 -epd 2nqknrb/1rpppppp/5B2/pp6/1PP1b3/3P4/P3PPPP/1RNQKNRB w GBg - 1 9 +epd 2nqknrb/1rpppppp/5B2/pp6/1PP1b3/3P4/P3PPPP/1RNQKNRB w GBg - perft 1 35 perft 2 1042 perft 3 36238 @@ -4637,7 +4637,7 @@ perft 5 38505058 perft 6 1202668717 id 515 -epd rb1nqknr/1pp1pppp/8/3p4/p2P4/6PN/PPPQPP1P/RBBN1K1R w HAha - 0 9 +epd rb1nqknr/1pp1pppp/8/3p4/p2P4/6PN/PPPQPP1P/RBBN1K1R w HAha - perft 1 29 perft 2 692 perft 3 21237 @@ -4646,7 +4646,7 @@ perft 5 17820605 perft 6 497251206 id 516 -epd rnbbqknr/pppp4/5p2/4p1pp/P7/2N2PP1/1PPPP2P/R1BBQKNR w HAha - 0 9 +epd rnbbqknr/pppp4/5p2/4p1pp/P7/2N2PP1/1PPPP2P/R1BBQKNR w HAha - perft 1 23 perft 2 595 perft 3 14651 @@ -4655,7 +4655,7 @@ perft 5 10881112 perft 6 329010121 id 517 -epd rn1qkbnr/p1p1pp1p/bp4p1/3p4/1P6/4P3/P1PP1PPP/RNBQKBNR w HAha - 0 9 +epd rn1qkbnr/p1p1pp1p/bp4p1/3p4/1P6/4P3/P1PP1PPP/RNBQKBNR w HAha - perft 1 30 perft 2 794 perft 3 24319 @@ -4664,7 +4664,7 @@ perft 5 21657601 perft 6 647745807 id 518 -epd r1bqk1rb/pppnpppp/5n2/3p4/2P3PP/2N5/PP1PPP2/R1BQKNRB w GAga - 1 9 +epd r1bqk1rb/pppnpppp/5n2/3p4/2P3PP/2N5/PP1PPP2/R1BQKNRB w GAga - perft 1 32 perft 2 821 perft 3 27121 @@ -4673,7 +4673,7 @@ perft 5 24923473 perft 6 710765657 id 519 -epd rbnqbknr/1p1ppp1p/6p1/p1p5/7P/3P4/PPP1PPP1/RBNQBKNR w HAha - 0 9 +epd rbnqbknr/1p1ppp1p/6p1/p1p5/7P/3P4/PPP1PPP1/RBNQBKNR w HAha - perft 1 24 perft 2 720 perft 3 18842 @@ -4682,7 +4682,7 @@ perft 5 15992882 perft 6 501093456 id 520 -epd r1qbbk1r/pp1ppppp/n1p5/5n2/B1P3P1/8/PP1PPP1P/RNQ1BKNR w HAha - 0 9 +epd r1qbbk1r/pp1ppppp/n1p5/5n2/B1P3P1/8/PP1PPP1P/RNQ1BKNR w HAha - perft 1 27 perft 2 831 perft 3 22293 @@ -4691,7 +4691,7 @@ perft 5 19948650 perft 6 637973209 id 521 -epd rnqkbb1r/p1pppppp/8/8/1p4n1/PP4PP/2PPPP2/RNQKBBNR w HAha - 0 9 +epd rnqkbb1r/p1pppppp/8/8/1p4n1/PP4PP/2PPPP2/RNQKBBNR w HAha - perft 1 18 perft 2 463 perft 3 9519 @@ -4700,7 +4700,7 @@ perft 5 6065231 perft 6 172734380 id 522 -epd rnqk1nrb/pppbpp2/7p/3p2p1/4B3/2N1N1P1/PPPPPP1P/R1QKB1R1 w GAga - 0 9 +epd rnqk1nrb/pppbpp2/7p/3p2p1/4B3/2N1N1P1/PPPPPP1P/R1QKB1R1 w GAga - perft 1 34 perft 2 1171 perft 3 38128 @@ -4709,7 +4709,7 @@ perft 5 42109356 perft 6 1465473753 id 523 -epd rbnqknbr/1pp1ppp1/3p4/7p/p2P2PP/2P5/PP2PP2/RBNQKNBR w HAha - 0 9 +epd rbnqknbr/1pp1ppp1/3p4/7p/p2P2PP/2P5/PP2PP2/RBNQKNBR w HAha - perft 1 32 perft 2 867 perft 3 28342 @@ -4718,7 +4718,7 @@ perft 5 26632459 perft 6 781067145 id 524 -epd rn1bknbr/pq2pppp/1p6/2pp4/P7/1P1P4/2PNPPPP/RNQBK1BR w HAha - 0 9 +epd rn1bknbr/pq2pppp/1p6/2pp4/P7/1P1P4/2PNPPPP/RNQBK1BR w HAha - perft 1 24 perft 2 627 perft 3 16652 @@ -4727,7 +4727,7 @@ perft 5 13200921 perft 6 385193532 id 525 -epd r1qk1bbr/ppp1pp1p/2np1n2/6p1/2PP4/3BP3/PP3PPP/RNQKN1BR w HAha - 2 9 +epd r1qk1bbr/ppp1pp1p/2np1n2/6p1/2PP4/3BP3/PP3PPP/RNQKN1BR w HAha - perft 1 31 perft 2 992 perft 3 30213 @@ -4736,7 +4736,7 @@ perft 5 30397368 perft 6 1011631987 id 526 -epd r1qknrbb/pppp1p2/2n3p1/4p2p/8/QPP5/P1NPPPPP/RN1K1RBB w FAfa - 2 9 +epd r1qknrbb/pppp1p2/2n3p1/4p2p/8/QPP5/P1NPPPPP/RN1K1RBB w FAfa - perft 1 30 perft 2 702 perft 3 21563 @@ -4745,7 +4745,7 @@ perft 5 16813114 perft 6 438096194 id 527 -epd bbkr1qnr/2pppppp/2n5/pp6/8/PPN5/1BPPPPPP/1BR1KQNR w HC - 2 9 +epd bbkr1qnr/2pppppp/2n5/pp6/8/PPN5/1BPPPPPP/1BR1KQNR w HC - perft 1 25 perft 2 573 perft 3 15183 @@ -4754,7 +4754,7 @@ perft 5 10554668 perft 6 283975400 id 528 -epd 1rnbkqnr/1bpppppp/1p6/7P/p2P4/5P2/PPP1P1P1/BRNBKQNR w HBhb - 0 9 +epd 1rnbkqnr/1bpppppp/1p6/7P/p2P4/5P2/PPP1P1P1/BRNBKQNR w HBhb - perft 1 21 perft 2 503 perft 3 11790 @@ -4763,7 +4763,7 @@ perft 5 7679979 perft 6 207799378 id 529 -epd brnkqbnr/2p1pppp/1p6/3p4/1pP5/P6P/3PPPP1/BRNKQBNR w HBhb - 0 9 +epd brnkqbnr/2p1pppp/1p6/3p4/1pP5/P6P/3PPPP1/BRNKQBNR w HBhb - perft 1 28 perft 2 743 perft 3 21054 @@ -4772,7 +4772,7 @@ perft 5 17354516 perft 6 507176753 id 530 -epd br1kqnrb/npp1pppp/8/3p4/p4N2/PP6/2PPPPPP/BR1KQNRB w GBgb - 0 9 +epd br1kqnrb/npp1pppp/8/3p4/p4N2/PP6/2PPPPPP/BR1KQNRB w GBgb - perft 1 31 perft 2 808 perft 3 25585 @@ -4781,7 +4781,7 @@ perft 5 22376575 perft 6 640362920 id 531 -epd rbbnkq1r/pppppp1p/7n/6p1/P5P1/2P2N2/1P1PPP1P/RBBNKQ1R w HAha - 1 9 +epd rbbnkq1r/pppppp1p/7n/6p1/P5P1/2P2N2/1P1PPP1P/RBBNKQ1R w HAha - perft 1 29 perft 2 580 perft 3 17585 @@ -4790,7 +4790,7 @@ perft 5 12730970 perft 6 325226128 id 532 -epd rnbbk1nr/pp2qppp/2ppp3/8/3P4/P1N4N/1PP1PPPP/R1BBKQ1R w HAha - 0 9 +epd rnbbk1nr/pp2qppp/2ppp3/8/3P4/P1N4N/1PP1PPPP/R1BBKQ1R w HAha - perft 1 29 perft 2 838 perft 3 24197 @@ -4799,7 +4799,7 @@ perft 5 21100580 perft 6 646624429 id 533 -epd rnbk1b1r/ppppn1pp/4pp2/7q/7P/P5PB/1PPPPP2/RNBKQ1NR w HAha - 3 9 +epd rnbk1b1r/ppppn1pp/4pp2/7q/7P/P5PB/1PPPPP2/RNBKQ1NR w HAha - perft 1 20 perft 2 729 perft 3 16633 @@ -4808,7 +4808,7 @@ perft 5 14507076 perft 6 498621813 id 534 -epd r2kqnrb/pbppppp1/np5p/8/4Q1P1/3P4/PPP1PP1P/RNBK1NRB w GAga - 2 9 +epd r2kqnrb/pbppppp1/np5p/8/4Q1P1/3P4/PPP1PP1P/RNBK1NRB w GAga - perft 1 47 perft 2 1219 perft 3 55009 @@ -4817,7 +4817,7 @@ perft 5 65239153 perft 6 1834391369 id 535 -epd rbnkbq1r/p1p2ppp/1p2pn2/3p4/P3P3/3P4/1PP1KPPP/RBN1BQNR w ha - 2 9 +epd rbnkbq1r/p1p2ppp/1p2pn2/3p4/P3P3/3P4/1PP1KPPP/RBN1BQNR w ha - perft 1 29 perft 2 923 perft 3 27179 @@ -4826,7 +4826,7 @@ perft 5 26202752 perft 6 868565895 id 536 -epd rk1bb1nr/ppppqppp/n7/1N2p3/6P1/7N/PPPPPP1P/R1KBBQ1R w HA - 6 9 +epd rk1bb1nr/ppppqppp/n7/1N2p3/6P1/7N/PPPPPP1P/R1KBBQ1R w HA - perft 1 27 perft 2 703 perft 3 19478 @@ -4835,7 +4835,7 @@ perft 5 16049807 perft 6 492966455 id 537 -epd rnkqbbnr/p1ppp2p/1p4p1/8/1B3p1P/2NP4/PPP1PPP1/R1KQ1BNR w HAha - 0 9 +epd rnkqbbnr/p1ppp2p/1p4p1/8/1B3p1P/2NP4/PPP1PPP1/R1KQ1BNR w HAha - perft 1 29 perft 2 610 perft 3 18855 @@ -4844,7 +4844,7 @@ perft 5 14020041 perft 6 355083962 id 538 -epd rnkqb1rb/pp1p1ppp/4p3/2P3n1/8/1PP5/P3PPPP/RNKQBNRB w GAga - 0 9 +epd rnkqb1rb/pp1p1ppp/4p3/2P3n1/8/1PP5/P3PPPP/RNKQBNRB w GAga - perft 1 29 perft 2 675 perft 3 20699 @@ -4853,7 +4853,7 @@ perft 5 17000613 perft 6 476598337 id 539 -epd rb1kqnbr/pp1pp1p1/1np2p2/7p/P1P3PP/8/1P1PPP2/RBNKQNBR w HAha - 0 9 +epd rb1kqnbr/pp1pp1p1/1np2p2/7p/P1P3PP/8/1P1PPP2/RBNKQNBR w HAha - perft 1 31 perft 2 1077 perft 3 33661 @@ -4862,7 +4862,7 @@ perft 5 37415304 perft 6 1328374620 id 540 -epd rnkbq1br/ppp2ppp/3p4/Q3p1n1/5P2/3P2P1/PPP1P2P/RNKB1NBR w HAha - 0 9 +epd rnkbq1br/ppp2ppp/3p4/Q3p1n1/5P2/3P2P1/PPP1P2P/RNKB1NBR w HAha - perft 1 41 perft 2 1201 perft 3 46472 @@ -4871,7 +4871,7 @@ perft 5 52991625 perft 6 1675608008 id 541 -epd rn1qnbbr/pp2pppp/2ppk3/8/2PP4/3Q1N2/PP2PPPP/RNK2BBR w HA - 1 9 +epd rn1qnbbr/pp2pppp/2ppk3/8/2PP4/3Q1N2/PP2PPPP/RNK2BBR w HA - perft 1 34 perft 2 666 perft 3 22474 @@ -4880,7 +4880,7 @@ perft 5 15860369 perft 6 353831792 id 542 -epd rnkqnr1b/ppppp1pp/5p2/8/Q1P2P2/8/PP1P2PP/RbK1NRBB w FAfa - 0 9 +epd rnkqnr1b/ppppp1pp/5p2/8/Q1P2P2/8/PP1P2PP/RbK1NRBB w FAfa - perft 1 36 perft 2 876 perft 3 31987 @@ -4889,7 +4889,7 @@ perft 5 29022529 perft 6 736717252 id 543 -epd bbrn1nqr/ppp1k1pp/5p2/3pp3/7P/3PN3/PPP1PPP1/BBRK1NQR w - - 1 9 +epd bbrn1nqr/ppp1k1pp/5p2/3pp3/7P/3PN3/PPP1PPP1/BBRK1NQR w - - perft 1 24 perft 2 583 perft 3 15063 @@ -4898,7 +4898,7 @@ perft 5 10522064 perft 6 280707118 id 544 -epd brnbkn1r/1pppp1p1/4q3/p4p1p/7P/1N3P2/PPPPP1PQ/BR1BKN1R w HBhb - 2 9 +epd brnbkn1r/1pppp1p1/4q3/p4p1p/7P/1N3P2/PPPPP1PQ/BR1BKN1R w HBhb - perft 1 27 perft 2 935 perft 3 26120 @@ -4907,7 +4907,7 @@ perft 5 26000648 perft 6 873063158 id 545 -epd br1knbqr/pp2p1pp/1n6/2pp1p2/6P1/2P4B/PP1PPPQP/BRNKN2R w HBhb - 0 9 +epd br1knbqr/pp2p1pp/1n6/2pp1p2/6P1/2P4B/PP1PPPQP/BRNKN2R w HBhb - perft 1 27 perft 2 681 perft 3 19202 @@ -4916,7 +4916,7 @@ perft 5 14954779 perft 6 415624943 id 546 -epd brnk1qrb/p1ppppp1/1p5p/8/P3n3/1N4P1/1PPPPPRP/BR1KNQ1B w Bgb - 0 9 +epd brnk1qrb/p1ppppp1/1p5p/8/P3n3/1N4P1/1PPPPPRP/BR1KNQ1B w Bgb - perft 1 22 perft 2 638 perft 3 13991 @@ -4925,7 +4925,7 @@ perft 5 9760752 perft 6 293499724 id 547 -epd rbbnknqr/pppp3p/5pp1/8/1P1pP3/7P/P1P2PP1/RBBNKNQR w HAha - 0 9 +epd rbbnknqr/pppp3p/5pp1/8/1P1pP3/7P/P1P2PP1/RBBNKNQR w HAha - perft 1 29 perft 2 756 perft 3 21616 @@ -4934,7 +4934,7 @@ perft 5 17602252 perft 6 528140595 id 548 -epd 1nbbknqr/rpp1ppp1/1Q1p3p/p7/2P2PP1/8/PP1PP2P/RNBBKN1R w HAh - 2 9 +epd 1nbbknqr/rpp1ppp1/1Q1p3p/p7/2P2PP1/8/PP1PP2P/RNBBKN1R w HAh - perft 1 37 perft 2 977 perft 3 34977 @@ -4943,7 +4943,7 @@ perft 5 33695089 perft 6 940198007 id 549 -epd rnb2bqr/ppkpppp1/3n3p/2p5/6PP/2N2P2/PPPPP3/R1BKNBQR w HA - 2 9 +epd rnb2bqr/ppkpppp1/3n3p/2p5/6PP/2N2P2/PPPPP3/R1BKNBQR w HA - perft 1 30 perft 2 647 perft 3 20365 @@ -4952,7 +4952,7 @@ perft 5 15115531 perft 6 369257622 id 550 -epd rn1k1qrb/p1pppppp/bp6/8/4n3/P4BPP/1PPPPP2/RNBKNQR1 w GAga - 2 9 +epd rn1k1qrb/p1pppppp/bp6/8/4n3/P4BPP/1PPPPP2/RNBKNQR1 w GAga - perft 1 22 perft 2 670 perft 3 14998 @@ -4961,7 +4961,7 @@ perft 5 11199653 perft 6 339919682 id 551 -epd rb2bnqr/nppkpppp/3p4/p7/1P6/P2N2P1/2PPPP1P/RB1KBNQR w HA - 3 9 +epd rb2bnqr/nppkpppp/3p4/p7/1P6/P2N2P1/2PPPP1P/RB1KBNQR w HA - perft 1 22 perft 2 479 perft 3 11475 @@ -4970,7 +4970,7 @@ perft 5 6831555 perft 6 167329117 id 552 -epd r1kbb1qr/2pppppp/np2n3/p7/2P3P1/8/PP1PPPQP/RNKBBN1R w HAha - 1 9 +epd r1kbb1qr/2pppppp/np2n3/p7/2P3P1/8/PP1PPPQP/RNKBBN1R w HAha - perft 1 32 perft 2 723 perft 3 23953 @@ -4979,7 +4979,7 @@ perft 5 19472074 perft 6 504622114 id 553 -epd rnknbb1r/p1ppp1pp/8/1p1P1p1q/8/P1P5/1P2PPPP/RNKNBBQR w HAha - 1 9 +epd rnknbb1r/p1ppp1pp/8/1p1P1p1q/8/P1P5/1P2PPPP/RNKNBBQR w HAha - perft 1 19 perft 2 607 perft 3 12733 @@ -4988,7 +4988,7 @@ perft 5 9753617 perft 6 325177085 id 554 -epd rnkn1qrb/pp1bp1pp/2p5/1N1p1p2/8/2P5/PPKPPPPP/R2NBQRB w ga - 2 9 +epd rnkn1qrb/pp1bp1pp/2p5/1N1p1p2/8/2P5/PPKPPPPP/R2NBQRB w ga - perft 1 27 perft 2 533 perft 3 14549 @@ -4997,7 +4997,7 @@ perft 5 9206957 perft 6 232664675 id 555 -epd r1nknqbr/pp2p1pp/2p2p2/3p4/6P1/PP1P4/2P1PP1b/RBNKNQBR w HAha - 0 9 +epd r1nknqbr/pp2p1pp/2p2p2/3p4/6P1/PP1P4/2P1PP1b/RBNKNQBR w HAha - perft 1 20 perft 2 582 perft 3 13777 @@ -5006,7 +5006,7 @@ perft 5 10708639 perft 6 326565393 id 556 -epd rnkb1qbr/p1pp1p1p/1p2pn2/1Q4p1/4P3/N4P2/PPPP2PP/R1KBN1BR w HAha - 0 9 +epd rnkb1qbr/p1pp1p1p/1p2pn2/1Q4p1/4P3/N4P2/PPPP2PP/R1KBN1BR w HAha - perft 1 40 perft 2 1038 perft 3 39356 @@ -5015,7 +5015,7 @@ perft 5 39145902 perft 6 1079612614 id 557 -epd rn2qbbr/1pkppp1p/p3n1p1/8/8/2P2P2/PP1PP1PP/RNKN1BBR w HA - 0 9 +epd rn2qbbr/1pkppp1p/p3n1p1/8/8/2P2P2/PP1PP1PP/RNKN1BBR w HA - perft 1 24 perft 2 605 perft 3 14888 @@ -5024,7 +5024,7 @@ perft 5 9687507 perft 6 260874068 id 558 -epd rn1nqrbb/p1kppp1p/8/1pp3p1/1P6/2N1P3/P1PP1PPP/RK1NQRBB w - - 0 9 +epd rn1nqrbb/p1kppp1p/8/1pp3p1/1P6/2N1P3/P1PP1PPP/RK1NQRBB w - - perft 1 21 perft 2 540 perft 3 12489 @@ -5033,7 +5033,7 @@ perft 5 8436136 perft 6 237525904 id 559 -epd bbrnknrq/1pp3pp/p2p1p2/4p3/P7/1P2N3/2PPPPPP/BBRN1RKQ w gc - 0 9 +epd bbrnknrq/1pp3pp/p2p1p2/4p3/P7/1P2N3/2PPPPPP/BBRN1RKQ w gc - perft 1 24 perft 2 527 perft 3 13900 @@ -5042,7 +5042,7 @@ perft 5 9139962 perft 6 226253685 id 560 -epd brnb1nrq/pppp1kpp/4p3/8/5p1P/P1P3P1/1P1PPP2/BRNBKNRQ w GB - 1 9 +epd brnb1nrq/pppp1kpp/4p3/8/5p1P/P1P3P1/1P1PPP2/BRNBKNRQ w GB - perft 1 29 perft 2 773 perft 3 23904 @@ -5051,7 +5051,7 @@ perft 5 20503775 perft 6 560338709 id 561 -epd br1k1brq/ppppp2p/1n1n1pp1/8/P1P5/3P2P1/1P2PP1P/BRNKNBRQ w GBgb - 0 9 +epd br1k1brq/ppppp2p/1n1n1pp1/8/P1P5/3P2P1/1P2PP1P/BRNKNBRQ w GBgb - perft 1 28 perft 2 811 perft 3 23550 @@ -5060,7 +5060,7 @@ perft 5 19913758 perft 6 565143976 id 562 -epd 1r1knrqb/n1pppppp/p1b5/1p6/8/3N1P2/PPPPP1PP/BRNK1RQB w fb - 3 9 +epd 1r1knrqb/n1pppppp/p1b5/1p6/8/3N1P2/PPPPP1PP/BRNK1RQB w fb - perft 1 29 perft 2 753 perft 3 23210 @@ -5069,7 +5069,7 @@ perft 5 20044474 perft 6 558383603 id 563 -epd rbbnk1rq/pppppppp/8/3Pn3/8/4P1P1/PPP2P1P/RBBNKNRQ w GAga - 1 9 +epd rbbnk1rq/pppppppp/8/3Pn3/8/4P1P1/PPP2P1P/RBBNKNRQ w GAga - perft 1 22 perft 2 551 perft 3 12619 @@ -5078,7 +5078,7 @@ perft 5 8204171 perft 6 217689974 id 564 -epd rnbbk1rq/2pppp1p/p3n1p1/1p6/P3N3/8/1PPPPPPP/RNBB1KRQ w ga - 0 9 +epd rnbbk1rq/2pppp1p/p3n1p1/1p6/P3N3/8/1PPPPPPP/RNBB1KRQ w ga - perft 1 26 perft 2 742 perft 3 20061 @@ -5087,7 +5087,7 @@ perft 5 16787080 perft 6 525678162 id 565 -epd rnbkn1rq/ppppppb1/6p1/7p/2B2P2/1P2P3/P1PP2PP/RNBKN1RQ w GAga - 1 9 +epd rnbkn1rq/ppppppb1/6p1/7p/2B2P2/1P2P3/P1PP2PP/RNBKN1RQ w GAga - perft 1 28 perft 2 799 perft 3 23210 @@ -5096,7 +5096,7 @@ perft 5 20755098 perft 6 639632905 id 566 -epd rn1knrqb/p2pppp1/b1p5/1p5p/2P2P2/1P6/P2PP1PP/RNBKNRQB w FAfa - 1 9 +epd rn1knrqb/p2pppp1/b1p5/1p5p/2P2P2/1P6/P2PP1PP/RNBKNRQB w FAfa - perft 1 30 perft 2 579 perft 3 18481 @@ -5105,7 +5105,7 @@ perft 5 13257198 perft 6 311282465 id 567 -epd rbnkbnrq/pp2p1Np/2p2p2/8/3p4/8/PPPPPPPP/RBNKBR1Q w Aga - 0 9 +epd rbnkbnrq/pp2p1Np/2p2p2/8/3p4/8/PPPPPPPP/RBNKBR1Q w Aga - perft 1 23 perft 2 670 perft 3 16435 @@ -5114,7 +5114,7 @@ perft 5 13012378 perft 6 411860744 id 568 -epd rk1bbnrq/ppp1pppp/n7/3p4/5P2/3P2NP/PPP1P1P1/RNKBB1RQ w GA - 0 9 +epd rk1bbnrq/ppp1pppp/n7/3p4/5P2/3P2NP/PPP1P1P1/RNKBB1RQ w GA - perft 1 26 perft 2 597 perft 3 16238 @@ -5123,7 +5123,7 @@ perft 5 11269462 perft 6 296701249 id 569 -epd r1knbbrq/pppp2p1/2n1p2p/5p2/4P3/P1PP4/1P3PPP/RNKNBBRQ w GAga - 1 9 +epd r1knbbrq/pppp2p1/2n1p2p/5p2/4P3/P1PP4/1P3PPP/RNKNBBRQ w GAga - perft 1 20 perft 2 596 perft 3 13091 @@ -5132,7 +5132,7 @@ perft 5 9416862 perft 6 293659781 id 570 -epd rnknbrqb/p1p1pp1p/3p4/1p1N2p1/8/N7/PPPPPPPP/1RK1BRQB w Ffa - 0 9 +epd rnknbrqb/p1p1pp1p/3p4/1p1N2p1/8/N7/PPPPPPPP/1RK1BRQB w Ffa - perft 1 26 perft 2 724 perft 3 18942 @@ -5141,7 +5141,7 @@ perft 5 15257204 perft 6 461293885 id 571 -epd rbnknrb1/1p1ppp1p/p1p3p1/8/1P3P2/1R6/PqPPP1PP/RBNKN1BQ w Afa - 0 9 +epd rbnknrb1/1p1ppp1p/p1p3p1/8/1P3P2/1R6/PqPPP1PP/RBNKN1BQ w Afa - perft 1 31 perft 2 1183 perft 3 34723 @@ -5150,7 +5150,7 @@ perft 5 38722152 perft 6 1421492227 id 572 -epd rnkbnrbq/2p1ppp1/p7/1p1p3p/3P4/1P4P1/P1P1PP1P/RNKBNRBQ w FAfa - 0 9 +epd rnkbnrbq/2p1ppp1/p7/1p1p3p/3P4/1P4P1/P1P1PP1P/RNKBNRBQ w FAfa - perft 1 24 perft 2 506 perft 3 12748 @@ -5159,7 +5159,7 @@ perft 5 8086100 perft 6 207129256 id 573 -epd r1knrbbq/pp1ppppp/2p1n3/8/2P3P1/P7/1PKPPP1P/RN1NRBBQ w ea - 0 9 +epd r1knrbbq/pp1ppppp/2p1n3/8/2P3P1/P7/1PKPPP1P/RN1NRBBQ w ea - perft 1 28 perft 2 570 perft 3 16037 @@ -5168,7 +5168,7 @@ perft 5 10278695 perft 6 242592363 id 574 -epd rnknrq1b/ppp1p1p1/4b3/3p1p1p/6P1/P4P2/1PPPPQ1P/RNKNR1BB w EAea - 2 9 +epd rnknrq1b/ppp1p1p1/4b3/3p1p1p/6P1/P4P2/1PPPPQ1P/RNKNR1BB w EAea - perft 1 30 perft 2 739 perft 3 23124 @@ -5177,7 +5177,7 @@ perft 5 19252739 perft 6 521629794 id 575 -epd bbqr1krn/pppp1p1p/5n2/4p1p1/3P4/P3QP2/1PP1P1PP/BB1RNKRN w GDgd - 0 9 +epd bbqr1krn/pppp1p1p/5n2/4p1p1/3P4/P3QP2/1PP1P1PP/BB1RNKRN w GDgd - perft 1 31 perft 2 799 perft 3 25627 @@ -5186,7 +5186,7 @@ perft 5 22172123 perft 6 609277274 id 576 -epd bq1b1krn/pp1ppppp/3n4/2r5/3p3N/6N1/PPP1PPPP/BQRB1KR1 w GCg - 2 9 +epd bq1b1krn/pp1ppppp/3n4/2r5/3p3N/6N1/PPP1PPPP/BQRB1KR1 w GCg - perft 1 21 perft 2 798 perft 3 18571 @@ -5195,7 +5195,7 @@ perft 5 17546069 perft 6 647165916 id 577 -epd bqrnkbrn/2pp1pp1/p7/1p2p2p/1P6/4N3/P1PPPPPP/BQR1KBRN w GCgc - 0 9 +epd bqrnkbrn/2pp1pp1/p7/1p2p2p/1P6/4N3/P1PPPPPP/BQR1KBRN w GCgc - perft 1 27 perft 2 783 perft 3 22327 @@ -5204,7 +5204,7 @@ perft 5 20059741 perft 6 624462073 id 578 -epd bqr1krnb/1np1pppp/8/pp1p4/8/2P2N2/PP1PPPPP/BQRNKR1B w FCfc - 0 9 +epd bqr1krnb/1np1pppp/8/pp1p4/8/2P2N2/PP1PPPPP/BQRNKR1B w FCfc - perft 1 28 perft 2 636 perft 3 18874 @@ -5213,7 +5213,7 @@ perft 5 14237097 perft 6 372181570 id 579 -epd qbb1rkrn/1ppppppp/p7/7n/8/P2P4/1PP1PPPP/QBBRNKRN w Gg - 0 9 +epd qbb1rkrn/1ppppppp/p7/7n/8/P2P4/1PP1PPPP/QBBRNKRN w Gg - perft 1 25 perft 2 547 perft 3 13837 @@ -5222,7 +5222,7 @@ perft 5 8849383 perft 6 229112926 id 580 -epd 1rbbnkrn/p1p1pp1p/2q5/1p1p2p1/8/2P3P1/PP1PPP1P/QRBBNKRN w GBgb - 2 9 +epd 1rbbnkrn/p1p1pp1p/2q5/1p1p2p1/8/2P3P1/PP1PPP1P/QRBBNKRN w GBgb - perft 1 24 perft 2 1010 perft 3 24370 @@ -5231,7 +5231,7 @@ perft 5 24328258 perft 6 961371180 id 581 -epd qrb1kbrn/ppp1p2p/4npp1/3p4/8/1PP4P/PR1PPPP1/Q1BNKBRN w Ggb - 1 9 +epd qrb1kbrn/ppp1p2p/4npp1/3p4/8/1PP4P/PR1PPPP1/Q1BNKBRN w Ggb - perft 1 18 perft 2 451 perft 3 9291 @@ -5240,7 +5240,7 @@ perft 5 5568106 perft 6 155744022 id 582 -epd qr2krnb/p1p1pppp/b1np4/1p6/3NP3/7P/PPPP1PP1/QRBNKR1B w FBfb - 2 9 +epd qr2krnb/p1p1pppp/b1np4/1p6/3NP3/7P/PPPP1PP1/QRBNKR1B w FBfb - perft 1 25 perft 2 667 perft 3 17081 @@ -5249,7 +5249,7 @@ perft 5 12458875 perft 6 361495148 id 583 -epd qbrnbkrn/ppp3pp/3p4/5p2/2P1pP2/6PP/PP1PP3/QBRNBKRN w GCgc - 0 9 +epd qbrnbkrn/ppp3pp/3p4/5p2/2P1pP2/6PP/PP1PP3/QBRNBKRN w GCgc - perft 1 24 perft 2 650 perft 3 16835 @@ -5258,7 +5258,7 @@ perft 5 12187382 perft 6 326834539 id 584 -epd qrnb1krn/ppp1p1pp/5p2/2Np4/b2P4/2P5/PP2PPPP/QR1BBKRN w GBgb - 0 9 +epd qrnb1krn/ppp1p1pp/5p2/2Np4/b2P4/2P5/PP2PPPP/QR1BBKRN w GBgb - perft 1 27 perft 2 641 perft 3 17490 @@ -5267,7 +5267,7 @@ perft 5 12103076 perft 6 310695797 id 585 -epd qrnkbbrn/pp2pp2/8/2pp2pp/6PP/3P4/PPPKPP2/QRN1BBRN w gb - 0 9 +epd qrnkbbrn/pp2pp2/8/2pp2pp/6PP/3P4/PPPKPP2/QRN1BBRN w gb - perft 1 22 perft 2 554 perft 3 13116 @@ -5276,7 +5276,7 @@ perft 5 9014737 perft 6 258925091 id 586 -epd qrnkbrnb/p1p1ppp1/1p6/3p4/3P3p/5N1P/PPP1PPP1/QRNKBR1B w FBfb - 0 9 +epd qrnkbrnb/p1p1ppp1/1p6/3p4/3P3p/5N1P/PPP1PPP1/QRNKBR1B w FBfb - perft 1 24 perft 2 529 perft 3 13205 @@ -5285,7 +5285,7 @@ perft 5 8295874 perft 6 213856651 id 587 -epd qbr1krbn/1pppp1pp/p7/5pn1/2PP4/8/PPB1PPPP/Q1RNKRBN w FCfc - 0 9 +epd qbr1krbn/1pppp1pp/p7/5pn1/2PP4/8/PPB1PPPP/Q1RNKRBN w FCfc - perft 1 26 perft 2 831 perft 3 21651 @@ -5294,7 +5294,7 @@ perft 5 18961456 perft 6 621884383 id 588 -epd 1rnbkrbn/1qp1pppp/3p4/pp6/4P3/1NP4P/PP1P1PP1/QR1BKRBN w FBfb - 0 9 +epd 1rnbkrbn/1qp1pppp/3p4/pp6/4P3/1NP4P/PP1P1PP1/QR1BKRBN w FBfb - perft 1 24 perft 2 597 perft 3 15089 @@ -5303,7 +5303,7 @@ perft 5 10832084 perft 6 307793179 id 589 -epd q1rkrbbn/ppp1pppp/8/3p4/1PnP4/P7/1RP1PPPP/Q1NKRBBN w Ee - 1 9 +epd q1rkrbbn/ppp1pppp/8/3p4/1PnP4/P7/1RP1PPPP/Q1NKRBBN w Ee - perft 1 20 perft 2 520 perft 3 10769 @@ -5312,7 +5312,7 @@ perft 5 6452205 perft 6 170268300 id 590 -epd qrnkrn1b/ppppp1pp/4b3/7P/6p1/P7/1PPPPP2/QRNKRNBB w EBeb - 0 9 +epd qrnkrn1b/ppppp1pp/4b3/7P/6p1/P7/1PPPPP2/QRNKRNBB w EBeb - perft 1 26 perft 2 566 perft 3 15623 @@ -5321,7 +5321,7 @@ perft 5 10940750 perft 6 287987207 id 591 -epd bbr1nkrn/ppp1pppp/3q4/3p4/8/P7/1PPPPPPP/BBRQNRKN w gc - 5 9 +epd bbr1nkrn/ppp1pppp/3q4/3p4/8/P7/1PPPPPPP/BBRQNRKN w gc - perft 1 19 perft 2 661 perft 3 13895 @@ -5330,7 +5330,7 @@ perft 5 10870247 perft 6 356399665 id 592 -epd brqbnkrn/pp1pp2p/5pp1/2p5/4P3/P2P1N2/1PP2PPP/BRQB1KRN w GBgb - 0 9 +epd brqbnkrn/pp1pp2p/5pp1/2p5/4P3/P2P1N2/1PP2PPP/BRQB1KRN w GBgb - perft 1 27 perft 2 679 perft 3 19916 @@ -5339,7 +5339,7 @@ perft 5 16391730 perft 6 455940859 id 593 -epd 2qnkbrn/p1pppppp/8/1r6/1p2bP2/7N/PPPPP1PP/BR1QKBRN w GBg - 4 9 +epd 2qnkbrn/p1pppppp/8/1r6/1p2bP2/7N/PPPPP1PP/BR1QKBRN w GBg - perft 1 18 perft 2 774 perft 3 15713 @@ -5348,7 +5348,7 @@ perft 5 14371755 perft 6 559579332 id 594 -epd r1qnkr1b/p1pppppp/7n/1p6/8/1P3b1N/PRPPPPPP/B1QNK1RB w f - 5 9 +epd r1qnkr1b/p1pppppp/7n/1p6/8/1P3b1N/PRPPPPPP/B1QNK1RB w f - perft 1 21 perft 2 677 perft 3 15437 @@ -5357,7 +5357,7 @@ perft 5 12463801 perft 6 410795298 id 595 -epd rbbqn1rn/pppp1pp1/3k4/4p2Q/2PPP3/8/PP3PPP/RBB1NKRN w GA - 1 9 +epd rbbqn1rn/pppp1pp1/3k4/4p2Q/2PPP3/8/PP3PPP/RBB1NKRN w GA - perft 1 40 perft 2 742 perft 3 28757 @@ -5366,7 +5366,7 @@ perft 5 21852196 perft 6 471452088 id 596 -epd rqbbnkrn/3pppp1/p1p4p/1p6/5P2/P2N4/1PPPP1PP/RQBBK1RN w ga - 0 9 +epd rqbbnkrn/3pppp1/p1p4p/1p6/5P2/P2N4/1PPPP1PP/RQBBK1RN w ga - perft 1 23 perft 2 665 perft 3 16400 @@ -5375,7 +5375,7 @@ perft 5 12794736 perft 6 396640086 id 597 -epd r2nkbrn/pp2pppp/8/2ppqb2/2P3P1/5P2/PP1PPN1P/RQB1KBRN w GAga - 3 9 +epd r2nkbrn/pp2pppp/8/2ppqb2/2P3P1/5P2/PP1PPN1P/RQB1KBRN w GAga - perft 1 28 perft 2 1108 perft 3 31164 @@ -5384,7 +5384,7 @@ perft 5 34780853 perft 6 1292405738 id 598 -epd rqbnk1nb/p1pppr1p/5p2/1p4p1/1PP1P3/8/P2P1PPP/RQBNKRNB w FAa - 1 9 +epd rqbnk1nb/p1pppr1p/5p2/1p4p1/1PP1P3/8/P2P1PPP/RQBNKRNB w FAa - perft 1 26 perft 2 650 perft 3 18208 @@ -5393,7 +5393,7 @@ perft 5 14565370 perft 6 416833400 id 599 -epd rbqnb1rn/p1pp1kpp/1p2pp2/8/4P2P/P5P1/1PPP1P2/RBQNBKRN w GA - 0 9 +epd rbqnb1rn/p1pp1kpp/1p2pp2/8/4P2P/P5P1/1PPP1P2/RBQNBKRN w GA - perft 1 20 perft 2 437 perft 3 9423 @@ -5402,7 +5402,7 @@ perft 5 5282124 perft 6 132309824 id 600 -epd rqnbbkrn/p1p1pppp/3p4/1p5B/8/1P1NP3/P1PP1PPP/RQ2BKRN w GAga - 0 9 +epd rqnbbkrn/p1p1pppp/3p4/1p5B/8/1P1NP3/P1PP1PPP/RQ2BKRN w GAga - perft 1 30 perft 2 606 perft 3 18382 @@ -5411,7 +5411,7 @@ perft 5 12989786 perft 6 326601372 id 601 -epd rqnkbbr1/ppppp1pp/5p2/7n/8/2PNP2P/PP1P1PP1/RQ1KBBRN w GAga - 1 9 +epd rqnkbbr1/ppppp1pp/5p2/7n/8/2PNP2P/PP1P1PP1/RQ1KBBRN w GAga - perft 1 23 perft 2 482 perft 3 12506 @@ -5420,7 +5420,7 @@ perft 5 8430874 perft 6 217797292 id 602 -epd r1nkbrnb/2ppppp1/1q6/pp5p/1P6/P3P3/2PPKPPP/RQN1BRNB w fa - 2 9 +epd r1nkbrnb/2ppppp1/1q6/pp5p/1P6/P3P3/2PPKPPP/RQN1BRNB w fa - perft 1 25 perft 2 827 perft 3 21518 @@ -5429,7 +5429,7 @@ perft 5 19290675 perft 6 632892337 id 603 -epd rbqnkrbn/p1ppppp1/7p/1p6/7P/2N1P3/PPPP1PPB/RBQ1KR1N w FAfa - 1 9 +epd rbqnkrbn/p1ppppp1/7p/1p6/7P/2N1P3/PPPP1PPB/RBQ1KR1N w FAfa - perft 1 30 perft 2 627 perft 3 18566 @@ -5438,7 +5438,7 @@ perft 5 12976682 perft 6 337377291 id 604 -epd r1nbkrbn/p1qp1ppp/8/1pp1p3/2P1P3/6P1/PP1PBP1P/RQN1KRBN w FAfa - 2 9 +epd r1nbkrbn/p1qp1ppp/8/1pp1p3/2P1P3/6P1/PP1PBP1P/RQN1KRBN w FAfa - perft 1 22 perft 2 616 perft 3 14503 @@ -5447,7 +5447,7 @@ perft 5 10850952 perft 6 335943324 id 605 -epd rqnkr1bn/ppp1ppb1/3p2pp/8/P7/2P2P2/1PKPP1PP/RQN1RBBN w ea - 1 9 +epd rqnkr1bn/ppp1ppb1/3p2pp/8/P7/2P2P2/1PKPP1PP/RQN1RBBN w ea - perft 1 31 perft 2 679 perft 3 21365 @@ -5456,7 +5456,7 @@ perft 5 15661072 perft 6 379844460 id 606 -epd r2krnbb/qppp1ppp/1n6/p3p3/PP6/4N3/N1PPPPPP/RQ1KR1BB w EAea - 4 9 +epd r2krnbb/qppp1ppp/1n6/p3p3/PP6/4N3/N1PPPPPP/RQ1KR1BB w EAea - perft 1 24 perft 2 645 perft 3 17054 @@ -5465,7 +5465,7 @@ perft 5 13837270 perft 6 416239106 id 607 -epd bbr1qk1n/1ppppp1p/2n5/p7/P7/1P2P3/2PP1PrP/1BRNQKRN w GCc - 0 9 +epd bbr1qk1n/1ppppp1p/2n5/p7/P7/1P2P3/2PP1PrP/1BRNQKRN w GCc - perft 1 18 perft 2 520 perft 3 10680 @@ -5474,7 +5474,7 @@ perft 5 7215306 perft 6 207612575 id 608 -epd brnbq1rn/2ppppkp/p5p1/1p6/8/1BP3P1/PP1PPP1P/BRN1QRKN w - - 0 9 +epd brnbq1rn/2ppppkp/p5p1/1p6/8/1BP3P1/PP1PPP1P/BRN1QRKN w - - perft 1 21 perft 2 625 perft 3 13989 @@ -5483,7 +5483,7 @@ perft 5 9929336 perft 6 300902534 id 609 -epd brn1kbrn/pp2p1pp/3p4/q1p2p2/2P4P/6P1/PP1PPP2/BRNQKBRN w GBgb - 1 9 +epd brn1kbrn/pp2p1pp/3p4/q1p2p2/2P4P/6P1/PP1PPP2/BRNQKBRN w GBgb - perft 1 18 perft 2 477 perft 3 10205 @@ -5492,7 +5492,7 @@ perft 5 6720181 perft 6 187205941 id 610 -epd brn1krnb/p3pppp/1qpp4/1p6/2P3P1/1P6/P2PPP1P/BRNQKRNB w FBfb - 1 9 +epd brn1krnb/p3pppp/1qpp4/1p6/2P3P1/1P6/P2PPP1P/BRNQKRNB w FBfb - perft 1 30 perft 2 835 perft 3 24761 @@ -5501,7 +5501,7 @@ perft 5 21806428 perft 6 654487872 id 611 -epd r1b1qkrn/1p1ppppp/p1p1n3/8/4P3/1PN5/P1PPQPPb/RBB2KRN w GAga - 0 9 +epd r1b1qkrn/1p1ppppp/p1p1n3/8/4P3/1PN5/P1PPQPPb/RBB2KRN w GAga - perft 1 28 perft 2 825 perft 3 24536 @@ -5510,7 +5510,7 @@ perft 5 22079005 perft 6 647939781 id 612 -epd r1bbqk1n/p1pppprp/n7/1p4p1/5P2/2N3N1/PPPPP1PP/1RBBQKR1 w Ga - 4 9 +epd r1bbqk1n/p1pppprp/n7/1p4p1/5P2/2N3N1/PPPPP1PP/1RBBQKR1 w Ga - perft 1 25 perft 2 545 perft 3 14657 @@ -5519,7 +5519,7 @@ perft 5 10271111 perft 6 273864588 id 613 -epd rnbqkbrn/p1pp1pp1/4p3/7p/2p4P/2P5/PP1PPPP1/R1BQKBRN w GAga - 0 9 +epd rnbqkbrn/p1pp1pp1/4p3/7p/2p4P/2P5/PP1PPPP1/R1BQKBRN w GAga - perft 1 17 perft 2 445 perft 3 9076 @@ -5528,7 +5528,7 @@ perft 5 5918310 perft 6 174733195 id 614 -epd rnbqkrnb/1p1pp1p1/2p4p/p4p2/3P2P1/7N/PPPBPP1P/RN1QKR1B w FAfa - 0 9 +epd rnbqkrnb/1p1pp1p1/2p4p/p4p2/3P2P1/7N/PPPBPP1P/RN1QKR1B w FAfa - perft 1 34 perft 2 746 perft 3 25319 @@ -5537,7 +5537,7 @@ perft 5 21285553 perft 6 569141201 id 615 -epd rbnqbkr1/1ppppp2/p5n1/6pp/4P3/1N6/PPPP1PPP/RBQ1BRKN w ga - 2 9 +epd rbnqbkr1/1ppppp2/p5n1/6pp/4P3/1N6/PPPP1PPP/RBQ1BRKN w ga - perft 1 18 perft 2 466 perft 3 9683 @@ -5546,7 +5546,7 @@ perft 5 6051500 perft 6 170135726 id 616 -epd rnqb1krn/ppppp1p1/7p/7b/P1P2pPP/8/1P1PPP2/RNQBBKRN w GAga - 0 9 +epd rnqb1krn/ppppp1p1/7p/7b/P1P2pPP/8/1P1PPP2/RNQBBKRN w GAga - perft 1 24 perft 2 575 perft 3 15400 @@ -5555,7 +5555,7 @@ perft 5 11039042 perft 6 291243811 id 617 -epd rnqkbbr1/p1pp1ppp/4p3/1p6/P3P2n/5P2/1PPP1NPP/RNQKBBR1 w GAga - 2 9 +epd rnqkbbr1/p1pp1ppp/4p3/1p6/P3P2n/5P2/1PPP1NPP/RNQKBBR1 w GAga - perft 1 27 perft 2 803 perft 3 22883 @@ -5564,7 +5564,7 @@ perft 5 20666099 perft 6 638696065 id 618 -epd rn1kbrnb/1qppp1pp/1p6/p4p2/1B1P4/1P5N/P1P1PPPP/RNQK1R1B w FAfa - 0 9 +epd rn1kbrnb/1qppp1pp/1p6/p4p2/1B1P4/1P5N/P1P1PPPP/RNQK1R1B w FAfa - perft 1 37 perft 2 1209 perft 3 43015 @@ -5573,7 +5573,7 @@ perft 5 49748034 perft 6 1671593862 id 619 -epd rbnqkrbn/Bppp1p2/p5pp/4p3/5P2/6PP/PPPPP3/RBNQKR1N w FAfa - 0 9 +epd rbnqkrbn/Bppp1p2/p5pp/4p3/5P2/6PP/PPPPP3/RBNQKR1N w FAfa - perft 1 29 perft 2 720 perft 3 20434 @@ -5582,7 +5582,7 @@ perft 5 15384362 perft 6 421343249 id 620 -epd rnqbkr1n/1p1ppbpp/3p1p2/p7/8/1P6/P1PPPPPP/R1QBKRBN w FAfa - 0 9 +epd rnqbkr1n/1p1ppbpp/3p1p2/p7/8/1P6/P1PPPPPP/R1QBKRBN w FAfa - perft 1 20 perft 2 657 perft 3 14424 @@ -5591,7 +5591,7 @@ perft 5 11843134 perft 6 413965054 id 621 -epd rnqkrb1n/ppppp3/6p1/5p1p/2b2P2/P1N5/1PPPP1PP/RQ1KRBBN w EAea - 1 9 +epd rnqkrb1n/ppppp3/6p1/5p1p/2b2P2/P1N5/1PPPP1PP/RQ1KRBBN w EAea - perft 1 28 perft 2 749 perft 3 20684 @@ -5600,7 +5600,7 @@ perft 5 15379233 perft 6 417191461 id 622 -epd rnqk1nbb/1pp2ppp/3pr3/p3p3/3P1P2/2N3N1/PPP1P1PP/R1QKR1BB w EAa - 1 9 +epd rnqk1nbb/1pp2ppp/3pr3/p3p3/3P1P2/2N3N1/PPP1P1PP/R1QKR1BB w EAa - perft 1 29 perft 2 883 perft 3 26412 @@ -5609,7 +5609,7 @@ perft 5 25144295 perft 6 789705382 id 623 -epd bbr1kqrn/p1p1ppp1/1p2n2p/3p4/1P1P4/2N5/P1P1PPPP/BBR1KQRN w GCgc - 0 9 +epd bbr1kqrn/p1p1ppp1/1p2n2p/3p4/1P1P4/2N5/P1P1PPPP/BBR1KQRN w GCgc - perft 1 22 perft 2 485 perft 3 11475 @@ -5618,7 +5618,7 @@ perft 5 6825123 perft 6 171793012 id 624 -epd brnbkq1n/ppp1ppr1/7p/3p2p1/2P3PP/8/PPBPPP2/BRN1KQRN w GBb - 2 9 +epd brnbkq1n/ppp1ppr1/7p/3p2p1/2P3PP/8/PPBPPP2/BRN1KQRN w GBb - perft 1 30 perft 2 634 perft 3 19017 @@ -5627,7 +5627,7 @@ perft 5 13674310 perft 6 345386924 id 625 -epd brnkqbr1/1pppp1pp/5p2/p7/P1P1P2n/8/1P1P1PP1/BRNKQBRN w GBgb - 0 9 +epd brnkqbr1/1pppp1pp/5p2/p7/P1P1P2n/8/1P1P1PP1/BRNKQBRN w GBgb - perft 1 21 perft 2 504 perft 3 11672 @@ -5636,7 +5636,7 @@ perft 5 7778289 perft 6 217596497 id 626 -epd b1rkqrnb/p1ppp1pp/1p1n4/5p2/5P2/PN5P/1PPPP1P1/BR1KQRNB w FBf - 0 9 +epd b1rkqrnb/p1ppp1pp/1p1n4/5p2/5P2/PN5P/1PPPP1P1/BR1KQRNB w FBf - perft 1 23 perft 2 688 perft 3 17259 @@ -5645,7 +5645,7 @@ perft 5 14228372 perft 6 451842354 id 627 -epd 1bbnkqrn/rppppp2/p5p1/7p/7P/P1P1P3/1P1P1PP1/RBBNKQRN w GAg - 1 9 +epd 1bbnkqrn/rppppp2/p5p1/7p/7P/P1P1P3/1P1P1PP1/RBBNKQRN w GAg - perft 1 25 perft 2 450 perft 3 12391 @@ -5654,7 +5654,7 @@ perft 5 7752404 perft 6 185393913 id 628 -epd rnbbkqr1/1pppppp1/7p/p3n3/PP5P/8/1BPPPPP1/RN1BKQRN w GAga - 0 9 +epd rnbbkqr1/1pppppp1/7p/p3n3/PP5P/8/1BPPPPP1/RN1BKQRN w GAga - perft 1 23 perft 2 543 perft 3 12224 @@ -5663,7 +5663,7 @@ perft 5 7549008 perft 6 199883770 id 629 -epd r1bkqbrn/ppppp1pp/8/5p2/3nPP2/1P4N1/P1PP2PP/RNBKQBR1 w GAga - 1 9 +epd r1bkqbrn/ppppp1pp/8/5p2/3nPP2/1P4N1/P1PP2PP/RNBKQBR1 w GAga - perft 1 27 perft 2 751 perft 3 21158 @@ -5672,7 +5672,7 @@ perft 5 17989920 perft 6 527273615 id 630 -epd rnbkqr1b/1p1pp1pp/p4p1n/2p5/1P5P/N4P2/P1PPP1P1/R1BKQRNB w FAfa - 0 9 +epd rnbkqr1b/1p1pp1pp/p4p1n/2p5/1P5P/N4P2/P1PPP1P1/R1BKQRNB w FAfa - perft 1 21 perft 2 498 perft 3 11738 @@ -5681,7 +5681,7 @@ perft 5 7808375 perft 6 216224115 id 631 -epd rbnkbqrn/p1p3pp/1p1p4/B3pp2/3P2P1/6N1/PPP1PP1P/RBNK1QR1 w GAga - 0 9 +epd rbnkbqrn/p1p3pp/1p1p4/B3pp2/3P2P1/6N1/PPP1PP1P/RBNK1QR1 w GAga - perft 1 34 perft 2 977 perft 3 33464 @@ -5690,7 +5690,7 @@ perft 5 33318567 perft 6 978991050 id 632 -epd r1kbbqrn/ppp3pp/2np1p2/1P2p3/3P1P2/8/P1P1P1PP/RNKBBQRN w GAga - 0 9 +epd r1kbbqrn/ppp3pp/2np1p2/1P2p3/3P1P2/8/P1P1P1PP/RNKBBQRN w GAga - perft 1 32 perft 2 920 perft 3 28916 @@ -5699,7 +5699,7 @@ perft 5 26763259 perft 6 797524786 id 633 -epd rk1qbbrn/p2npppp/1p6/2p4Q/8/4P3/PPPP1PPP/RNK1B1RN w GA - 2 9 +epd rk1qbbrn/p2npppp/1p6/2p4Q/8/4P3/PPPP1PPP/RNK1B1RN w GA - perft 1 35 perft 2 657 perft 3 22359 @@ -5708,7 +5708,7 @@ perft 5 16662477 perft 6 419496845 id 634 -epd rnk1brnb/pp1p1pp1/8/q1p1p2p/5P2/NP6/P1PPP1PP/R1KQBRNB w FAfa - 1 9 +epd rnk1brnb/pp1p1pp1/8/q1p1p2p/5P2/NP6/P1PPP1PP/R1KQBRNB w FAfa - perft 1 26 perft 2 774 perft 3 20215 @@ -5717,7 +5717,7 @@ perft 5 16987110 perft 6 523437649 id 635 -epd rb1kqrbn/npp1ppp1/p7/3P3p/2PP4/8/PP3PPP/RBNKQRBN w FAfa - 0 9 +epd rb1kqrbn/npp1ppp1/p7/3P3p/2PP4/8/PP3PPP/RBNKQRBN w FAfa - perft 1 35 perft 2 775 perft 3 27395 @@ -5726,7 +5726,7 @@ perft 5 23983464 perft 6 625669222 id 636 -epd rnkb1rbn/pp1p2pp/8/2p1pp1q/P6P/1PN5/2PPPPP1/R1KBQRBN w FAfa - 1 9 +epd rnkb1rbn/pp1p2pp/8/2p1pp1q/P6P/1PN5/2PPPPP1/R1KBQRBN w FAfa - perft 1 22 perft 2 899 perft 3 21188 @@ -5735,7 +5735,7 @@ perft 5 21518343 perft 6 857951339 id 637 -epd rnkqrbbn/1pppp1p1/8/p2N1p1p/2P4P/8/PP1PPPP1/R1KQRBBN w EAea - 0 9 +epd rnkqrbbn/1pppp1p1/8/p2N1p1p/2P4P/8/PP1PPPP1/R1KQRBBN w EAea - perft 1 29 perft 2 585 perft 3 17571 @@ -5744,7 +5744,7 @@ perft 5 12238776 perft 6 299752383 id 638 -epd rnk1r1bb/pp1ppppp/1q4n1/2p5/5P1P/3PP3/PPP3P1/RNKQRNBB w EAea - 1 9 +epd rnk1r1bb/pp1ppppp/1q4n1/2p5/5P1P/3PP3/PPP3P1/RNKQRNBB w EAea - perft 1 27 perft 2 884 perft 3 24613 @@ -5753,7 +5753,7 @@ perft 5 23698701 perft 6 790239502 id 639 -epd bbrnkrqn/1ppp1p2/6pp/p3p3/5PP1/2PB4/PP1PP2P/B1RNKRQN w FCfc - 0 9 +epd bbrnkrqn/1ppp1p2/6pp/p3p3/5PP1/2PB4/PP1PP2P/B1RNKRQN w FCfc - perft 1 37 perft 2 693 perft 3 25425 @@ -5762,7 +5762,7 @@ perft 5 20138432 perft 6 481498664 id 640 -epd b1rbkrqn/ppp2ppp/1n2p3/3p4/6P1/2PP4/PP2PP1P/BRNBKRQN w FBf - 1 9 +epd b1rbkrqn/ppp2ppp/1n2p3/3p4/6P1/2PP4/PP2PP1P/BRNBKRQN w FBf - perft 1 21 perft 2 463 perft 3 10610 @@ -5771,7 +5771,7 @@ perft 5 6307276 perft 6 159025909 id 641 -epd brnkrb1n/1pp1p1pp/3p4/p1Nq1p2/2P5/8/PP1PPPPP/BRK1RBQN w eb - 2 9 +epd brnkrb1n/1pp1p1pp/3p4/p1Nq1p2/2P5/8/PP1PPPPP/BRK1RBQN w eb - perft 1 27 perft 2 725 perft 3 17842 @@ -5780,7 +5780,7 @@ perft 5 12604078 perft 6 362747791 id 642 -epd brn1r1nb/ppppkppp/4p3/8/2PP1P2/8/PP1KP1PP/BRN1RQNB w - - 1 9 +epd brn1r1nb/ppppkppp/4p3/8/2PP1P2/8/PP1KP1PP/BRN1RQNB w - - perft 1 25 perft 2 623 perft 3 16874 @@ -5789,7 +5789,7 @@ perft 5 12290985 perft 6 317097424 id 643 -epd rbb1krqn/1pp1pp1p/p3n1p1/3pP3/8/1PN5/P1PP1PPP/RBB1KRQN w FAfa d6 0 9 +epd rbb1krqn/1pp1pp1p/p3n1p1/3pP3/8/1PN5/P1PP1PPP/RBB1KRQN w FAfa d6 perft 1 23 perft 2 529 perft 3 12641 @@ -5798,7 +5798,7 @@ perft 5 7861413 perft 6 202594556 id 644 -epd r1bbkrqn/p1pppppp/8/4n3/1p5P/P2P2P1/1PP1PP2/RNBBKRQN w FAfa - 0 9 +epd r1bbkrqn/p1pppppp/8/4n3/1p5P/P2P2P1/1PP1PP2/RNBBKRQN w FAfa - perft 1 23 perft 2 571 perft 3 13133 @@ -5807,7 +5807,7 @@ perft 5 8699448 perft 6 243460643 id 645 -epd rnbkrbqn/p1pp1ppp/4p3/1p6/8/BPN3P1/P1PPPP1P/R2KRBQN w EAea - 2 9 +epd rnbkrbqn/p1pp1ppp/4p3/1p6/8/BPN3P1/P1PPPP1P/R2KRBQN w EAea - perft 1 29 perft 2 692 perft 3 20014 @@ -5816,7 +5816,7 @@ perft 5 14904192 perft 6 386694739 id 646 -epd rnbkrqn1/pppppp2/8/1Q2b1pp/P3P3/5P2/1PPP2PP/RNBKR1NB w EAea - 0 9 +epd rnbkrqn1/pppppp2/8/1Q2b1pp/P3P3/5P2/1PPP2PP/RNBKR1NB w EAea - perft 1 37 perft 2 1001 perft 3 36440 @@ -5825,7 +5825,7 @@ perft 5 35626426 perft 6 993747544 id 647 -epd rbnkbrqn/p1pppp2/7p/1p4pP/3P1P2/8/PPP1P1P1/RBNKBRQN w FAfa - 0 9 +epd rbnkbrqn/p1pppp2/7p/1p4pP/3P1P2/8/PPP1P1P1/RBNKBRQN w FAfa - perft 1 30 perft 2 564 perft 3 17143 @@ -5834,7 +5834,7 @@ perft 5 11859538 perft 6 293703269 id 648 -epd 1nkbbrqn/3ppppp/r1p5/pp6/8/4PP2/PPPPN1PP/RNKBBRQ1 w FAf - 2 9 +epd 1nkbbrqn/3ppppp/r1p5/pp6/8/4PP2/PPPPN1PP/RNKBBRQ1 w FAf - perft 1 26 perft 2 546 perft 3 14641 @@ -5843,7 +5843,7 @@ perft 5 9556962 perft 6 245137199 id 649 -epd rnkrbbq1/pppppnp1/7p/8/1B1Q1p2/3P1P2/PPP1P1PP/RNKR1B1N w DAda - 2 9 +epd rnkrbbq1/pppppnp1/7p/8/1B1Q1p2/3P1P2/PPP1P1PP/RNKR1B1N w DAda - perft 1 43 perft 2 887 perft 3 36240 @@ -5852,7 +5852,7 @@ perft 5 33185346 perft 6 851927292 id 650 -epd 1rkrbqnb/pppppp2/2n3p1/7p/3P3P/P4N2/1PP1PPP1/RNKRBQ1B w DAd - 0 9 +epd 1rkrbqnb/pppppp2/2n3p1/7p/3P3P/P4N2/1PP1PPP1/RNKRBQ1B w DAd - perft 1 26 perft 2 622 perft 3 16049 @@ -5861,7 +5861,7 @@ perft 5 10786140 perft 6 285233838 id 651 -epd rbnkr1bn/pp1pqp1p/2p1p3/6p1/3P4/7P/PPP1PPP1/RBNKRQBN w EAea - 0 9 +epd rbnkr1bn/pp1pqp1p/2p1p3/6p1/3P4/7P/PPP1PPP1/RBNKRQBN w EAea - perft 1 19 perft 2 566 perft 3 12257 @@ -5870,7 +5870,7 @@ perft 5 9107175 perft 6 293397389 id 652 -epd r1kbrqb1/pppp2pp/2n1p1n1/5p1B/4PP2/P7/1PPP2PP/RNK1RQBN w EAea - 2 9 +epd r1kbrqb1/pppp2pp/2n1p1n1/5p1B/4PP2/P7/1PPP2PP/RNK1RQBN w EAea - perft 1 39 perft 2 1359 perft 3 53626 @@ -5879,7 +5879,7 @@ perft 5 73871486 perft 6 2633945690 id 653 -epd rnkrqbbn/p1p3pp/1p1ppp2/8/1P6/3P2P1/PKP1PP1P/RN1RQBBN w da - 0 9 +epd rnkrqbbn/p1p3pp/1p1ppp2/8/1P6/3P2P1/PKP1PP1P/RN1RQBBN w da - perft 1 26 perft 2 776 perft 3 20735 @@ -5888,7 +5888,7 @@ perft 5 16884013 perft 6 503561996 id 654 -epd rnkrqnbb/ppp2p1p/3p4/4p1p1/3P3P/N1Q5/PPP1PPP1/R1KR1NBB w DAda - 0 9 +epd rnkrqnbb/ppp2p1p/3p4/4p1p1/3P3P/N1Q5/PPP1PPP1/R1KR1NBB w DAda - perft 1 40 perft 2 1175 perft 3 45637 @@ -5897,7 +5897,7 @@ perft 5 52620163 perft 6 1633655838 id 655 -epd bbrnkrn1/p1pppp2/1p6/6pp/3q4/1P3QP1/P1PPPP1P/BBRNKRN1 w FCfc - 0 9 +epd bbrnkrn1/p1pppp2/1p6/6pp/3q4/1P3QP1/P1PPPP1P/BBRNKRN1 w FCfc - perft 1 34 perft 2 1398 perft 3 45749 @@ -5906,7 +5906,7 @@ perft 5 57268492 perft 6 2059942014 id 656 -epd br1bkrnq/1p2pppp/pnp5/3p4/P1P5/5P2/1P1PPKPP/BRNB1RNQ w fb - 2 9 +epd br1bkrnq/1p2pppp/pnp5/3p4/P1P5/5P2/1P1PPKPP/BRNB1RNQ w fb - perft 1 24 perft 2 501 perft 3 12237 @@ -5915,7 +5915,7 @@ perft 5 7049659 perft 6 177940764 id 657 -epd brnkrbn1/pppppp1q/B6p/6p1/8/1P2PP2/P1PP2PP/BRNKR1NQ w EBeb - 0 9 +epd brnkrbn1/pppppp1q/B6p/6p1/8/1P2PP2/P1PP2PP/BRNKR1NQ w EBeb - perft 1 34 perft 2 815 perft 3 25868 @@ -5924,7 +5924,7 @@ perft 5 22006883 perft 6 639803952 id 658 -epd br1krnqb/pppppp1p/1n4p1/8/8/P2NN3/2PPPPPP/BR1K1RQB w Beb - 2 9 +epd br1krnqb/pppppp1p/1n4p1/8/8/P2NN3/2PPPPPP/BR1K1RQB w Beb - perft 1 37 perft 2 1029 perft 3 36748 @@ -5933,7 +5933,7 @@ perft 5 36214583 perft 6 1026195877 id 659 -epd rbbnkr1q/p1p2ppp/1p1ppn2/8/1PP4P/8/P2PPPP1/RBBNKRNQ w FAfa - 0 9 +epd rbbnkr1q/p1p2ppp/1p1ppn2/8/1PP4P/8/P2PPPP1/RBBNKRNQ w FAfa - perft 1 28 perft 2 755 perft 3 22623 @@ -5942,7 +5942,7 @@ perft 5 18972778 perft 6 513486101 id 660 -epd r1b1krnq/pp2pppp/1bn5/2pp4/4N3/5P2/PPPPPRPP/R1BBK1NQ w Afa - 0 9 +epd r1b1krnq/pp2pppp/1bn5/2pp4/4N3/5P2/PPPPPRPP/R1BBK1NQ w Afa - perft 1 24 perft 2 705 perft 3 17427 @@ -5951,7 +5951,7 @@ perft 5 13532966 perft 6 426443376 id 661 -epd 1nbkrbn1/rpppppqp/p7/6p1/4P3/3P2P1/PPP1KP1P/RNB1RBNQ w e - 1 9 +epd 1nbkrbn1/rpppppqp/p7/6p1/4P3/3P2P1/PPP1KP1P/RNB1RBNQ w e - perft 1 31 perft 2 800 perft 3 24748 @@ -5960,7 +5960,7 @@ perft 5 21193292 perft 6 625757852 id 662 -epd r1bkrnqb/pp3ppp/n1ppp3/8/1P5P/P7/R1PPPPP1/1NBKRNQB w Eea - 0 9 +epd r1bkrnqb/pp3ppp/n1ppp3/8/1P5P/P7/R1PPPPP1/1NBKRNQB w Eea - perft 1 21 perft 2 482 perft 3 11417 @@ -5969,7 +5969,7 @@ perft 5 7112890 perft 6 180378139 id 663 -epd rbnkbrnq/ppp1p2p/5p2/3p2p1/1B1P4/1N4P1/PPP1PP1P/RB1K1RNQ w FAfa - 0 9 +epd rbnkbrnq/ppp1p2p/5p2/3p2p1/1B1P4/1N4P1/PPP1PP1P/RB1K1RNQ w FAfa - perft 1 33 perft 2 780 perft 3 25532 @@ -5978,7 +5978,7 @@ perft 5 20756770 perft 6 535497008 id 664 -epd rnk1brnq/pp1ppppp/2p5/b7/8/1P2P2P/P1PP1PPQ/RNKBBRN1 w FAfa - 3 9 +epd rnk1brnq/pp1ppppp/2p5/b7/8/1P2P2P/P1PP1PPQ/RNKBBRN1 w FAfa - perft 1 29 perft 2 648 perft 3 19043 @@ -5987,7 +5987,7 @@ perft 5 13722785 perft 6 341389148 id 665 -epd rnkrbbnq/p1p3pp/5p2/1p1pp3/P7/1PN2P2/2PPP1PP/R1KRBBNQ w DAda - 0 9 +epd rnkrbbnq/p1p3pp/5p2/1p1pp3/P7/1PN2P2/2PPP1PP/R1KRBBNQ w DAda - perft 1 26 perft 2 827 perft 3 21865 @@ -5996,7 +5996,7 @@ perft 5 18916370 perft 6 589161126 id 666 -epd r1krbnqb/p1pp1ppp/2n1p3/8/1p4P1/PPP5/3PPP1P/RNKRBNQB w DAda - 1 9 +epd r1krbnqb/p1pp1ppp/2n1p3/8/1p4P1/PPP5/3PPP1P/RNKRBNQB w DAda - perft 1 25 perft 2 540 perft 3 14709 @@ -6005,7 +6005,7 @@ perft 5 9491817 perft 6 225389422 id 667 -epd rbnkrnbq/ppp1pp2/3p2p1/2N5/P6p/2P5/1P1PPPPP/RB1KRNBQ w EAea - 0 9 +epd rbnkrnbq/ppp1pp2/3p2p1/2N5/P6p/2P5/1P1PPPPP/RB1KRNBQ w EAea - perft 1 32 perft 2 790 perft 3 25107 @@ -6014,7 +6014,7 @@ perft 5 20906017 perft 6 578332225 id 668 -epd rnkbrn1q/1ppppppb/8/p4N1p/8/P1N5/1PPPPPPP/R1KBR1BQ w EAea - 0 9 +epd rnkbrn1q/1ppppppb/8/p4N1p/8/P1N5/1PPPPPPP/R1KBR1BQ w EAea - perft 1 31 perft 2 691 perft 3 20813 @@ -6023,7 +6023,7 @@ perft 5 15308408 perft 6 404129987 id 669 -epd rnkrnbbq/p1p2ppp/3pp3/1p6/6P1/4PQ1B/PPPP1P1P/RNKRN1B1 w DAda - 0 9 +epd rnkrnbbq/p1p2ppp/3pp3/1p6/6P1/4PQ1B/PPPP1P1P/RNKRN1B1 w DAda - perft 1 29 perft 2 558 perft 3 16800 @@ -6032,7 +6032,7 @@ perft 5 10825379 perft 6 246965507 id 670 -epd rnkrnqbb/pp2p1p1/3p3p/2p2p2/5P2/1P1N4/P1PPPQPP/RNKR2BB w DAda - 0 9 +epd rnkrnqbb/pp2p1p1/3p3p/2p2p2/5P2/1P1N4/P1PPPQPP/RNKR2BB w DAda - perft 1 29 perft 2 762 perft 3 23210 @@ -6041,7 +6041,7 @@ perft 5 20522675 perft 6 596067005 id 671 -epd bb1rknnr/ppqppppp/8/2p5/3P1N2/1P6/P1P1PPPP/BBQRKN1R w HDhd - 1 9 +epd bb1rknnr/ppqppppp/8/2p5/3P1N2/1P6/P1P1PPPP/BBQRKN1R w HDhd - perft 1 33 perft 2 963 perft 3 32279 @@ -6050,7 +6050,7 @@ perft 5 34552118 perft 6 1124738493 id 672 -epd bqrbknnr/ppp1p2p/8/3p1p2/5p2/P3N2P/1PPPP1P1/BQRBK1NR w HChc - 0 9 +epd bqrbknnr/ppp1p2p/8/3p1p2/5p2/P3N2P/1PPPP1P1/BQRBK1NR w HChc - perft 1 20 perft 2 398 perft 3 9009 @@ -6059,7 +6059,7 @@ perft 5 4834319 perft 6 113660536 id 673 -epd b1rk1bnr/qpp1pppp/p4n2/3p4/3PPP2/7N/PPP3PP/BQRKNB1R w HChc - 1 9 +epd b1rk1bnr/qpp1pppp/p4n2/3p4/3PPP2/7N/PPP3PP/BQRKNB1R w HChc - perft 1 25 perft 2 648 perft 3 16587 @@ -6068,7 +6068,7 @@ perft 5 12200870 perft 6 351766307 id 674 -epd bqkrnnrb/pppp2p1/4pp2/4P2p/6P1/7P/PPPP1P2/BQRKNNRB w GC - 1 9 +epd bqkrnnrb/pppp2p1/4pp2/4P2p/6P1/7P/PPPP1P2/BQRKNNRB w GC - perft 1 30 perft 2 493 perft 3 15118 @@ -6077,7 +6077,7 @@ perft 5 8786998 perft 6 181492621 id 675 -epd q1brknnr/1p1ppppp/p7/2p5/8/1PPP4/P2RPPPP/QBB1KNNR w Hhd - 0 9 +epd q1brknnr/1p1ppppp/p7/2p5/8/1PPP4/P2RPPPP/QBB1KNNR w Hhd - perft 1 25 perft 2 501 perft 3 13206 @@ -6086,7 +6086,7 @@ perft 5 7982978 perft 6 192717198 id 676 -epd qrb1k1nr/ppppb1pp/6n1/4ppN1/3P4/4N3/PPP1PPPP/QRBBK2R w HBhb - 2 9 +epd qrb1k1nr/ppppb1pp/6n1/4ppN1/3P4/4N3/PPP1PPPP/QRBBK2R w HBhb - perft 1 31 perft 2 872 perft 3 26191 @@ -6095,7 +6095,7 @@ perft 5 22493014 perft 6 646855304 id 677 -epd 1rbknbnr/1ppp1pp1/q6p/p3p3/5P2/2PPB3/PP2P1PP/QR1KNBNR w HBhb - 0 9 +epd 1rbknbnr/1ppp1pp1/q6p/p3p3/5P2/2PPB3/PP2P1PP/QR1KNBNR w HBhb - perft 1 28 perft 2 1020 perft 3 28147 @@ -6104,7 +6104,7 @@ perft 5 27484692 perft 6 947786800 id 678 -epd qrbk2rb/1ppp1ppp/5nn1/p3p3/1N6/P7/1PPPPPPP/QRB1KNRB w gb - 0 9 +epd qrbk2rb/1ppp1ppp/5nn1/p3p3/1N6/P7/1PPPPPPP/QRB1KNRB w gb - perft 1 23 perft 2 592 perft 3 14398 @@ -6113,7 +6113,7 @@ perft 5 10098215 perft 6 293988585 id 679 -epd qbrk1nnr/1pp1pppp/2b5/p2p4/P2P2P1/8/1PP1PP1P/QBKRBNNR w hc - 1 9 +epd qbrk1nnr/1pp1pppp/2b5/p2p4/P2P2P1/8/1PP1PP1P/QBKRBNNR w hc - perft 1 26 perft 2 654 perft 3 18103 @@ -6122,7 +6122,7 @@ perft 5 13740891 perft 6 373081138 id 680 -epd qrkbbnnr/ppp2p1p/4p3/3p2p1/P7/2PP4/1P2PPPP/QRKBBNNR w HBhb - 0 9 +epd qrkbbnnr/ppp2p1p/4p3/3p2p1/P7/2PP4/1P2PPPP/QRKBBNNR w HBhb - perft 1 25 perft 2 626 perft 3 16616 @@ -6131,7 +6131,7 @@ perft 5 12079406 perft 6 324006164 id 681 -epd qr1kbbnr/ppp1pp1p/4n1p1/2Pp4/6P1/4N3/PP1PPP1P/QRK1BBNR w HB d6 0 9 +epd qr1kbbnr/ppp1pp1p/4n1p1/2Pp4/6P1/4N3/PP1PPP1P/QRK1BBNR w HB d6 perft 1 26 perft 2 699 perft 3 18068 @@ -6140,7 +6140,7 @@ perft 5 13353359 perft 6 375702908 id 682 -epd qrk1b1rb/p1pppppp/3nnQ2/1p6/1P3P2/3P4/P1P1P1PP/1RKNBNRB w GBgb - 3 9 +epd qrk1b1rb/p1pppppp/3nnQ2/1p6/1P3P2/3P4/P1P1P1PP/1RKNBNRB w GBgb - perft 1 43 perft 2 1369 perft 3 55463 @@ -6149,7 +6149,7 @@ perft 5 71514365 perft 6 2427477375 id 683 -epd qbrk1nbr/pppp3p/5n2/4ppp1/3P1P2/4N3/PPP1P1PP/QBKRN1BR w hc - 0 9 +epd qbrk1nbr/pppp3p/5n2/4ppp1/3P1P2/4N3/PPP1P1PP/QBKRN1BR w hc - perft 1 25 perft 2 752 perft 3 20165 @@ -6158,7 +6158,7 @@ perft 5 17493373 perft 6 543180234 id 684 -epd qrkb1nbr/1pppppQp/3n4/p7/5p2/1P1N4/P1PPP1PP/1RKB1NBR w HBhb - 0 9 +epd qrkb1nbr/1pppppQp/3n4/p7/5p2/1P1N4/P1PPP1PP/1RKB1NBR w HBhb - perft 1 45 perft 2 946 perft 3 40100 @@ -6167,7 +6167,7 @@ perft 5 39736157 perft 6 1051910977 id 685 -epd qrk1nbbr/ppp1p1p1/4n2p/3p1p2/1P5P/3P2P1/P1P1PP2/QRKNNBBR w HBhb - 1 9 +epd qrk1nbbr/ppp1p1p1/4n2p/3p1p2/1P5P/3P2P1/P1P1PP2/QRKNNBBR w HBhb - perft 1 32 perft 2 770 perft 3 25367 @@ -6176,7 +6176,7 @@ perft 5 21717615 perft 6 577979364 id 686 -epd qrkn1rbb/pp2pppp/2p5/3p4/P2Qn1P1/1P6/2PPPP1P/1RKNNRBB w FBfb - 0 9 +epd qrkn1rbb/pp2pppp/2p5/3p4/P2Qn1P1/1P6/2PPPP1P/1RKNNRBB w FBfb - perft 1 38 perft 2 943 perft 3 35335 @@ -6185,7 +6185,7 @@ perft 5 31909835 perft 6 798405123 id 687 -epd bbrqknnr/ppp4p/3pp3/5pp1/4PP2/5Q2/PPPP2PP/BBR1KNNR w HChc - 0 9 +epd bbrqknnr/ppp4p/3pp3/5pp1/4PP2/5Q2/PPPP2PP/BBR1KNNR w HChc - perft 1 36 perft 2 843 perft 3 29974 @@ -6194,7 +6194,7 @@ perft 5 26828059 perft 6 723306114 id 688 -epd 1rqbkn1r/p1p1pppp/1p5n/P2p4/3Pb1P1/8/1PP1PP1P/BRQBKNNR w HBhb - 0 9 +epd 1rqbkn1r/p1p1pppp/1p5n/P2p4/3Pb1P1/8/1PP1PP1P/BRQBKNNR w HBhb - perft 1 23 perft 2 778 perft 3 19482 @@ -6203,7 +6203,7 @@ perft 5 17337683 perft 6 579112676 id 689 -epd br1knbnr/1qp1pppp/pp1p4/8/8/PP6/2PPPPPP/BRQKNBNR w HBhb - 2 9 +epd br1knbnr/1qp1pppp/pp1p4/8/8/PP6/2PPPPPP/BRQKNBNR w HBhb - perft 1 26 perft 2 697 perft 3 18835 @@ -6212,7 +6212,7 @@ perft 5 15280079 perft 6 473071890 id 690 -epd brqk2rb/ppppp1pp/4np2/8/2n5/3P1Q2/PP2PPPP/BR1KNNRB w GBgb - 0 9 +epd brqk2rb/ppppp1pp/4np2/8/2n5/3P1Q2/PP2PPPP/BR1KNNRB w GBgb - perft 1 32 perft 2 948 perft 3 30434 @@ -6221,7 +6221,7 @@ perft 5 29821322 perft 6 874251866 id 691 -epd r1bqknnr/pp1pp1p1/5p1p/2p1b2N/2P5/8/PPQPPPPP/RBB1K1NR w HAha - 0 9 +epd r1bqknnr/pp1pp1p1/5p1p/2p1b2N/2P5/8/PPQPPPPP/RBB1K1NR w HAha - perft 1 31 perft 2 785 perft 3 25549 @@ -6230,7 +6230,7 @@ perft 5 22244193 perft 6 592797491 id 692 -epd rqbbknnr/ppppp2p/5pp1/8/8/1P3PP1/PQPPP2P/R1BBKNNR w HAha - 0 9 +epd rqbbknnr/ppppp2p/5pp1/8/8/1P3PP1/PQPPP2P/R1BBKNNR w HAha - perft 1 23 perft 2 391 perft 3 10163 @@ -6239,7 +6239,7 @@ perft 5 5576671 perft 6 121267576 id 693 -epd rqbknbnr/1pp1p2p/p7/3p1pp1/7N/1PP5/P2PPPPP/RQBK1BNR w HAha - 0 9 +epd rqbknbnr/1pp1p2p/p7/3p1pp1/7N/1PP5/P2PPPPP/RQBK1BNR w HAha - perft 1 27 perft 2 676 perft 3 19606 @@ -6248,7 +6248,7 @@ perft 5 15955388 perft 6 448477218 id 694 -epd rqb1nnrb/2ppkppp/1p2p3/p7/2PPP3/1P6/P4PPP/RQBKNNRB w GA - 1 9 +epd rqb1nnrb/2ppkppp/1p2p3/p7/2PPP3/1P6/P4PPP/RQBKNNRB w GA - perft 1 31 perft 2 727 perft 3 22895 @@ -6257,7 +6257,7 @@ perft 5 18361051 perft 6 483248153 id 695 -epd rb1kbn1r/p1ppppp1/qp5n/7p/P7/RPP5/3PPPPP/1BQKBNNR w Hha - 2 9 +epd rb1kbn1r/p1ppppp1/qp5n/7p/P7/RPP5/3PPPPP/1BQKBNNR w Hha - perft 1 29 perft 2 837 perft 3 23815 @@ -6266,7 +6266,7 @@ perft 5 21279560 perft 6 682863811 id 696 -epd rqkbb1nr/p1p2ppp/1p1p2n1/3Np3/4P3/5N2/PPPP1PPP/RQKBB2R w HAha - 0 9 +epd rqkbb1nr/p1p2ppp/1p1p2n1/3Np3/4P3/5N2/PPPP1PPP/RQKBB2R w HAha - perft 1 28 perft 2 717 perft 3 20663 @@ -6275,7 +6275,7 @@ perft 5 16347343 perft 6 453153783 id 697 -epd rqknbbr1/p1pppp1p/1p3np1/8/4P3/2P2P1P/PP1P2P1/RQKNBBNR w HAa - 0 9 +epd rqknbbr1/p1pppp1p/1p3np1/8/4P3/2P2P1P/PP1P2P1/RQKNBBNR w HAa - perft 1 27 perft 2 650 perft 3 18231 @@ -6284,7 +6284,7 @@ perft 5 13847463 perft 6 383256006 id 698 -epd r1k1bnrb/1qpppppp/1p2n3/p7/1P5P/6P1/P1PPPP2/RQKNBNR1 w GAga - 1 9 +epd r1k1bnrb/1qpppppp/1p2n3/p7/1P5P/6P1/P1PPPP2/RQKNBNR1 w GAga - perft 1 24 perft 2 806 perft 3 20693 @@ -6293,7 +6293,7 @@ perft 5 19382263 perft 6 686009788 id 699 -epd rb1knnbr/1pp1ppp1/p2p3p/5q2/3B2P1/3P1P2/PPP1P2P/RBQKNN1R w HAha - 0 9 +epd rb1knnbr/1pp1ppp1/p2p3p/5q2/3B2P1/3P1P2/PPP1P2P/RBQKNN1R w HAha - perft 1 34 perft 2 1360 perft 3 44096 @@ -6302,7 +6302,7 @@ perft 5 51973672 perft 6 1837704407 id 700 -epd rqkb1nbr/p1p1ppp1/1p3n1p/2Qp4/8/2P5/PP1PPPPP/R1KBNNBR w HAha - 2 9 +epd rqkb1nbr/p1p1ppp1/1p3n1p/2Qp4/8/2P5/PP1PPPPP/R1KBNNBR w HAha - perft 1 39 perft 2 983 perft 3 38218 @@ -6311,7 +6311,7 @@ perft 5 36347815 perft 6 918801645 id 701 -epd rqknnbbr/2pppp2/pp5p/6p1/1P1P4/4PP2/P1P3PP/RQKNNBBR w HAha - 0 9 +epd rqknnbbr/2pppp2/pp5p/6p1/1P1P4/4PP2/P1P3PP/RQKNNBBR w HAha - perft 1 26 perft 2 628 perft 3 17638 @@ -6320,7 +6320,7 @@ perft 5 13787303 perft 6 386125234 id 702 -epd rqkn1rbb/1pp1pppp/p7/3p4/3Pn3/2P1PP2/PP4PP/RQKNNRBB w FAfa - 1 9 +epd rqkn1rbb/1pp1pppp/p7/3p4/3Pn3/2P1PP2/PP4PP/RQKNNRBB w FAfa - perft 1 20 perft 2 527 perft 3 12216 @@ -6329,7 +6329,7 @@ perft 5 8082183 perft 6 219311659 id 703 -epd bbrkqn1r/1pppppp1/5n2/p7/1PP2P1p/7N/P2PP1PP/BBRKQN1R w HChc - 1 9 +epd bbrkqn1r/1pppppp1/5n2/p7/1PP2P1p/7N/P2PP1PP/BBRKQN1R w HChc - perft 1 36 perft 2 963 perft 3 35291 @@ -6338,7 +6338,7 @@ perft 5 35907489 perft 6 1034223364 id 704 -epd brkbqn1r/p2ppppp/7n/1p6/P1p3PP/8/1PPPPP1N/BRKBQ1NR w HBhb - 0 9 +epd brkbqn1r/p2ppppp/7n/1p6/P1p3PP/8/1PPPPP1N/BRKBQ1NR w HBhb - perft 1 18 perft 2 583 perft 3 11790 @@ -6347,7 +6347,7 @@ perft 5 8858385 perft 6 304339862 id 705 -epd brkq1bnr/pp1ppp1p/8/2p2np1/P7/8/1PPPPPPP/BRKQNBNR w HBhb - 0 9 +epd brkq1bnr/pp1ppp1p/8/2p2np1/P7/8/1PPPPPPP/BRKQNBNR w HBhb - perft 1 19 perft 2 552 perft 3 11811 @@ -6356,7 +6356,7 @@ perft 5 8432183 perft 6 262293169 id 706 -epd brkqnnrb/1ppppppp/8/8/p3P3/5N2/PPPP1PPP/BRKQ1NRB w GBgb - 3 9 +epd brkqnnrb/1ppppppp/8/8/p3P3/5N2/PPPP1PPP/BRKQ1NRB w GBgb - perft 1 21 perft 2 397 perft 3 9653 @@ -6365,7 +6365,7 @@ perft 5 5489836 perft 6 128389738 id 707 -epd rbbkq1nr/1p2pppp/p1p3nB/3p4/1Q1P4/6N1/PPP1PPPP/RB1K2NR w HAha - 0 9 +epd rbbkq1nr/1p2pppp/p1p3nB/3p4/1Q1P4/6N1/PPP1PPPP/RB1K2NR w HAha - perft 1 40 perft 2 1132 perft 3 43404 @@ -6374,7 +6374,7 @@ perft 5 47425783 perft 6 1415578783 id 708 -epd rkbbq1nr/1pppp1p1/4np2/p6p/8/PP3P2/1KPPP1PP/R1BBQNNR w ha - 0 9 +epd rkbbq1nr/1pppp1p1/4np2/p6p/8/PP3P2/1KPPP1PP/R1BBQNNR w ha - perft 1 24 perft 2 596 perft 3 15220 @@ -6383,7 +6383,7 @@ perft 5 10822049 perft 6 302056813 id 709 -epd r1bqn1nr/pkpppp1p/1p4pb/8/PN6/R7/1PPPPPPP/1KBQ1BNR w H - 2 9 +epd r1bqn1nr/pkpppp1p/1p4pb/8/PN6/R7/1PPPPPPP/1KBQ1BNR w H - perft 1 33 perft 2 794 perft 3 25450 @@ -6392,7 +6392,7 @@ perft 5 20919309 perft 6 561073410 id 710 -epd rkb1nnrb/1pppq1pp/p4p2/4p3/5P2/1P1PB3/P1P1P1PP/RK1QNNRB w GAga - 0 9 +epd rkb1nnrb/1pppq1pp/p4p2/4p3/5P2/1P1PB3/P1P1P1PP/RK1QNNRB w GAga - perft 1 26 perft 2 625 perft 3 17050 @@ -6401,7 +6401,7 @@ perft 5 12515042 perft 6 342967558 id 711 -epd rbkqbn1r/pppp1p1p/2n1p1p1/8/8/1P1PP1N1/P1P2PPP/RBKQB1NR w HAha - 1 9 +epd rbkqbn1r/pppp1p1p/2n1p1p1/8/8/1P1PP1N1/P1P2PPP/RBKQB1NR w HAha - perft 1 30 perft 2 660 perft 3 20308 @@ -6410,7 +6410,7 @@ perft 5 15348335 perft 6 403323883 id 712 -epd rkqbb1n1/pppppppr/8/6np/5P2/8/PPPPP1PP/RKQBBNNR w HAa - 6 9 +epd rkqbb1n1/pppppppr/8/6np/5P2/8/PPPPP1PP/RKQBBNNR w HAa - perft 1 23 perft 2 500 perft 3 12154 @@ -6419,7 +6419,7 @@ perft 5 7519117 perft 6 196524441 id 713 -epd rkqnbbnr/ppppppp1/8/7p/3N4/6PP/PPPPPP2/RKQNBB1R w HAa - 0 9 +epd rkqnbbnr/ppppppp1/8/7p/3N4/6PP/PPPPPP2/RKQNBB1R w HAa - perft 1 24 perft 2 484 perft 3 12495 @@ -6428,7 +6428,7 @@ perft 5 7775173 perft 6 193947530 id 714 -epd rkqnb1rb/p1p1pppp/1p1p4/2n5/3P4/2P1N1N1/PP2PPPP/RKQ1B1RB w GAga - 0 9 +epd rkqnb1rb/p1p1pppp/1p1p4/2n5/3P4/2P1N1N1/PP2PPPP/RKQ1B1RB w GAga - perft 1 28 perft 2 1020 perft 3 29124 @@ -6437,7 +6437,7 @@ perft 5 30515456 perft 6 1073711823 id 715 -epd rbk1nnbr/1ppq1ppp/p2p4/4p3/P3B2P/2P5/1P1PPPP1/R1KQNNBR w HAha - 2 9 +epd rbk1nnbr/1ppq1ppp/p2p4/4p3/P3B2P/2P5/1P1PPPP1/R1KQNNBR w HAha - perft 1 38 perft 2 998 perft 3 37265 @@ -6446,7 +6446,7 @@ perft 5 38552638 perft 6 1139322479 id 716 -epd r1qbn1br/k1pppppp/6n1/pp6/5P1P/P7/1PPPP1PB/RKQBNN1R w HA - 1 9 +epd r1qbn1br/k1pppppp/6n1/pp6/5P1P/P7/1PPPP1PB/RKQBNN1R w HA - perft 1 22 perft 2 549 perft 3 12867 @@ -6455,7 +6455,7 @@ perft 5 8725809 perft 6 251613569 id 717 -epd rkqnn1br/pppp3p/4p1pb/5p2/P2P4/7P/1PP1PPPB/RKQNNB1R w HAha - 1 9 +epd rkqnn1br/pppp3p/4p1pb/5p2/P2P4/7P/1PP1PPPB/RKQNNB1R w HAha - perft 1 32 perft 2 659 perft 3 21249 @@ -6464,7 +6464,7 @@ perft 5 15434721 perft 6 365761521 id 718 -epd rk1nnrbb/p1p1pppp/1p6/3p1q2/P3P3/2NN4/1PPP1PPP/RKQ2RBB w FAfa - 3 9 +epd rk1nnrbb/p1p1pppp/1p6/3p1q2/P3P3/2NN4/1PPP1PPP/RKQ2RBB w FAfa - perft 1 29 perft 2 989 perft 3 29087 @@ -6473,7 +6473,7 @@ perft 5 29643404 perft 6 998848556 id 719 -epd bbrk1q1r/ppppppp1/3n4/7p/3Pn3/6PN/PPP1PPNP/BBRK1Q1R w HChc - 2 9 +epd bbrk1q1r/ppppppp1/3n4/7p/3Pn3/6PN/PPP1PPNP/BBRK1Q1R w HChc - perft 1 23 perft 2 712 perft 3 16551 @@ -6482,7 +6482,7 @@ perft 5 12995202 perft 6 411077508 id 720 -epd brkbnq1r/p1ppp2p/5ppn/1p6/5P2/1P1P2P1/P1P1P2P/BRKBNQNR w HBhb - 0 9 +epd brkbnq1r/p1ppp2p/5ppn/1p6/5P2/1P1P2P1/P1P1P2P/BRKBNQNR w HBhb - perft 1 28 perft 2 856 perft 3 24984 @@ -6491,7 +6491,7 @@ perft 5 23529352 perft 6 754501112 id 721 -epd br1k1bnr/ppppp1pp/4np2/1B2P2q/3P4/8/PPP2PPP/BRKNQ1NR w HB - 3 9 +epd br1k1bnr/ppppp1pp/4np2/1B2P2q/3P4/8/PPP2PPP/BRKNQ1NR w HB - perft 1 36 perft 2 1214 perft 3 40615 @@ -6500,7 +6500,7 @@ perft 5 45096834 perft 6 1470987023 id 722 -epd brk1qnrb/pnppp1p1/1p6/5p1p/8/5PPP/PPPPP1R1/BRKNQN1B w Bgb - 0 9 +epd brk1qnrb/pnppp1p1/1p6/5p1p/8/5PPP/PPPPP1R1/BRKNQN1B w Bgb - perft 1 22 perft 2 551 perft 3 13111 @@ -6509,7 +6509,7 @@ perft 5 9040545 perft 6 259643605 id 723 -epd rbbkn1nr/1ppp2pp/p3p3/2q2p2/3P4/6P1/PPPBPP1P/RB1KNQNR w HAha - 0 9 +epd rbbkn1nr/1ppp2pp/p3p3/2q2p2/3P4/6P1/PPPBPP1P/RB1KNQNR w HAha - perft 1 31 perft 2 1060 perft 3 31332 @@ -6518,7 +6518,7 @@ perft 5 30314172 perft 6 976268967 id 724 -epd rkbbn1nr/ppppp1pp/8/6N1/5p2/1q6/P1PPPPPP/RKBBN1QR w HAha - 0 9 +epd rkbbn1nr/ppppp1pp/8/6N1/5p2/1q6/P1PPPPPP/RKBBN1QR w HAha - perft 1 3 perft 2 72 perft 3 1919 @@ -6527,7 +6527,7 @@ perft 5 1400832 perft 6 39654253 id 725 -epd rkb2bnr/pp2pppp/2p1n3/3p4/q2P4/5NP1/PPP1PP1P/RKBNQBR1 w Aha - 0 9 +epd rkb2bnr/pp2pppp/2p1n3/3p4/q2P4/5NP1/PPP1PP1P/RKBNQBR1 w Aha - perft 1 29 perft 2 861 perft 3 24504 @@ -6536,7 +6536,7 @@ perft 5 22763215 perft 6 731511256 id 726 -epd rkbq1nrb/ppppppp1/7p/8/1P1n4/P4P1P/2PPP1P1/RKBNQNRB w GAga - 0 9 +epd rkbq1nrb/ppppppp1/7p/8/1P1n4/P4P1P/2PPP1P1/RKBNQNRB w GAga - perft 1 25 perft 2 672 perft 3 17631 @@ -6545,7 +6545,7 @@ perft 5 12954224 perft 6 361237536 id 727 -epd rbknb1nr/ppp1qp1p/6p1/3pp3/3P3P/2B1P3/PPP2PP1/RBKN1QNR w HAha - 1 9 +epd rbknb1nr/ppp1qp1p/6p1/3pp3/3P3P/2B1P3/PPP2PP1/RBKN1QNR w HAha - perft 1 27 perft 2 857 perft 3 24688 @@ -6554,7 +6554,7 @@ perft 5 23790033 perft 6 768247869 id 728 -epd rknbbq1r/p1pppppp/1p2N3/8/3n4/2P5/PP1PPPPP/RK1BBQNR w HAha - 4 9 +epd rknbbq1r/p1pppppp/1p2N3/8/3n4/2P5/PP1PPPPP/RK1BBQNR w HAha - perft 1 29 perft 2 763 perft 3 22138 @@ -6563,7 +6563,7 @@ perft 5 16926075 perft 6 447896703 id 729 -epd r1nqbbnr/1pppp1pp/1k6/p4p2/8/4P3/PPPP1PPP/RKN1BBNR w HA - 0 9 +epd r1nqbbnr/1pppp1pp/1k6/p4p2/8/4P3/PPPP1PPP/RKN1BBNR w HA - perft 1 26 perft 2 658 perft 3 17302 @@ -6572,7 +6572,7 @@ perft 5 12380488 perft 6 349047256 id 730 -epd rkn2qrb/ppp1pppp/6n1/1b1p4/1P6/4PPB1/P1PP2PP/RKNQ1NRB w GAga - 3 9 +epd rkn2qrb/ppp1pppp/6n1/1b1p4/1P6/4PPB1/P1PP2PP/RKNQ1NRB w GAga - perft 1 23 perft 2 574 perft 3 14070 @@ -6581,7 +6581,7 @@ perft 5 9501401 perft 6 263870337 id 731 -epd rbkn2br/ppppp1p1/4np1p/1P5q/8/2P1N3/P2PPPPP/RBK1QNBR w HAha - 1 9 +epd rbkn2br/ppppp1p1/4np1p/1P5q/8/2P1N3/P2PPPPP/RBK1QNBR w HAha - perft 1 29 perft 2 992 perft 3 29506 @@ -6590,7 +6590,7 @@ perft 5 30148787 perft 6 1045942540 id 732 -epd 1knbqnbr/1ppppp1p/r5p1/p7/7P/2PN2P1/PP1PPP2/RK1BQNBR w HAh - 2 9 +epd 1knbqnbr/1ppppp1p/r5p1/p7/7P/2PN2P1/PP1PPP2/RK1BQNBR w HAh - perft 1 26 perft 2 698 perft 3 19395 @@ -6599,7 +6599,7 @@ perft 5 14848229 perft 6 402599313 id 733 -epd rk1qnbbr/pnpppp1p/6p1/1p6/3P4/1P6/P1P1PPPP/RKNQNBBR w HAha - 1 9 +epd rk1qnbbr/pnpppp1p/6p1/1p6/3P4/1P6/P1P1PPPP/RKNQNBBR w HAha - perft 1 20 perft 2 480 perft 3 11159 @@ -6608,7 +6608,7 @@ perft 5 7425917 perft 6 203194521 id 734 -epd rknqnrbb/pp1p2p1/5p1p/2p1p3/2P1P3/P2P4/1P3PPP/RKNQNRBB w FAfa - 0 9 +epd rknqnrbb/pp1p2p1/5p1p/2p1p3/2P1P3/P2P4/1P3PPP/RKNQNRBB w FAfa - perft 1 26 perft 2 679 perft 3 18116 @@ -6617,7 +6617,7 @@ perft 5 13790137 perft 6 392629571 id 735 -epd bbrk2qr/pp1p1ppp/3n2n1/2p1p3/3P1P2/6N1/PPP1P1PP/BBRKN1QR w HChc - 0 9 +epd bbrk2qr/pp1p1ppp/3n2n1/2p1p3/3P1P2/6N1/PPP1P1PP/BBRKN1QR w HChc - perft 1 26 perft 2 790 perft 3 21521 @@ -6626,7 +6626,7 @@ perft 5 19259490 perft 6 617563700 id 736 -epd b1krnnqr/1p1ppppp/p1p5/b6B/P7/4P1N1/1PPP1PPP/BRK1N1QR w HB - 2 9 +epd b1krnnqr/1p1ppppp/p1p5/b6B/P7/4P1N1/1PPP1PPP/BRK1N1QR w HB - perft 1 26 perft 2 625 perft 3 16451 @@ -6635,7 +6635,7 @@ perft 5 11490615 perft 6 304805107 id 737 -epd 1rknnbqr/3ppppp/p7/1pp5/4b2P/P4P2/1PPPP1PR/BRKNNBQ1 w Bhb - 1 9 +epd 1rknnbqr/3ppppp/p7/1pp5/4b2P/P4P2/1PPPP1PR/BRKNNBQ1 w Bhb - perft 1 24 perft 2 757 perft 3 19746 @@ -6644,7 +6644,7 @@ perft 5 17275100 perft 6 544309489 id 738 -epd br1nn1rb/pppkpqpp/3p1p2/8/PP6/4N3/1KPPPPPP/BR2NQRB w - - 3 9 +epd br1nn1rb/pppkpqpp/3p1p2/8/PP6/4N3/1KPPPPPP/BR2NQRB w - - perft 1 24 perft 2 682 perft 3 17129 @@ -6653,7 +6653,7 @@ perft 5 13057308 perft 6 375033550 id 739 -epd rbbkn1qr/pppp2p1/6np/4pp2/7N/7P/PPPPPPPR/RBBK1NQ1 w Aha - 0 9 +epd rbbkn1qr/pppp2p1/6np/4pp2/7N/7P/PPPPPPPR/RBBK1NQ1 w Aha - perft 1 22 perft 2 586 perft 3 14158 @@ -6662,7 +6662,7 @@ perft 5 10607781 perft 6 324452612 id 740 -epd rk1bn1qr/pppbpppp/4n3/4p3/4P3/5P2/PPPP2PP/RKBB1NQR w HAha - 1 9 +epd rk1bn1qr/pppbpppp/4n3/4p3/4P3/5P2/PPPP2PP/RKBB1NQR w HAha - perft 1 22 perft 2 530 perft 3 13440 @@ -6671,7 +6671,7 @@ perft 5 9514787 perft 6 259898748 id 741 -epd rkbnnbqr/1ppp1ppp/p7/4p3/8/QP3P2/P1PPP1PP/RKBNNB1R w HAha - 0 9 +epd rkbnnbqr/1ppp1ppp/p7/4p3/8/QP3P2/P1PPP1PP/RKBNNB1R w HAha - perft 1 29 perft 2 705 perft 3 21511 @@ -6680,7 +6680,7 @@ perft 5 17524731 perft 6 472356665 id 742 -epd 1kbnnqrb/1pp1p1pp/r4p2/p2p4/N4P2/3P4/PPP1P1PP/RKB1NQRB w GAg - 2 9 +epd 1kbnnqrb/1pp1p1pp/r4p2/p2p4/N4P2/3P4/PPP1P1PP/RKB1NQRB w GAg - perft 1 21 perft 2 623 perft 3 14979 @@ -6689,7 +6689,7 @@ perft 5 11601134 perft 6 343214006 id 743 -epd rbknbn1r/pppp1p1p/4p1q1/8/P1P3Pp/8/1P1PPP2/RBKNBNQR w HAha - 0 9 +epd rbknbn1r/pppp1p1p/4p1q1/8/P1P3Pp/8/1P1PPP2/RBKNBNQR w HAha - perft 1 30 perft 2 813 perft 3 24959 @@ -6698,7 +6698,7 @@ perft 5 23379040 perft 6 692576573 id 744 -epd rk1bb1qr/2pppppp/p2nn3/1p4P1/6QP/8/PPPPPP2/RKNBBN1R w HAha - 2 9 +epd rk1bb1qr/2pppppp/p2nn3/1p4P1/6QP/8/PPPPPP2/RKNBBN1R w HAha - perft 1 36 perft 2 857 perft 3 30124 @@ -6707,7 +6707,7 @@ perft 5 26485812 perft 6 696999449 id 745 -epd rkn1bbqr/p2ppppp/2p1n3/1p6/4PP2/6PP/PPPP4/RKNNBBQR w HAha - 0 9 +epd rkn1bbqr/p2ppppp/2p1n3/1p6/4PP2/6PP/PPPP4/RKNNBBQR w HAha - perft 1 33 perft 2 687 perft 3 22744 @@ -6716,7 +6716,7 @@ perft 5 17101732 perft 6 412778368 id 746 -epd rkn1bqrb/pnp1pppp/3p4/8/Pp6/1N2NP2/1PPPP1PP/RK2BQRB w GAga - 0 9 +epd rkn1bqrb/pnp1pppp/3p4/8/Pp6/1N2NP2/1PPPP1PP/RK2BQRB w GAga - perft 1 28 perft 2 591 perft 3 17174 @@ -6725,7 +6725,7 @@ perft 5 12182448 perft 6 312575205 id 747 -epd rbk1n1br/ppp1ppqp/2n5/2Np2p1/8/2P5/PPBPPPPP/R1KN1QBR w HAha - 4 9 +epd rbk1n1br/ppp1ppqp/2n5/2Np2p1/8/2P5/PPBPPPPP/R1KN1QBR w HAha - perft 1 35 perft 2 930 perft 3 30663 @@ -6734,7 +6734,7 @@ perft 5 27160490 perft 6 780616047 id 748 -epd rknbn1br/1ppp1ppp/p3p3/8/1q6/2P2N1P/P2PPPP1/RKNB1QBR w HAha - 0 9 +epd rknbn1br/1ppp1ppp/p3p3/8/1q6/2P2N1P/P2PPPP1/RKNB1QBR w HAha - perft 1 4 perft 2 157 perft 3 3697 @@ -6743,7 +6743,7 @@ perft 5 3454704 perft 6 125373395 id 749 -epd rkn1qbbr/pp3ppp/4n3/2ppp3/4P1P1/P2P4/1PP2P1P/RKNNQBBR w HAha - 0 9 +epd rkn1qbbr/pp3ppp/4n3/2ppp3/4P1P1/P2P4/1PP2P1P/RKNNQBBR w HAha - perft 1 28 perft 2 840 perft 3 24437 @@ -6752,7 +6752,7 @@ perft 5 23200961 perft 6 756489357 id 750 -epd rkn1qrbb/pp1ppp2/2p1n1p1/7p/2P2P1P/6P1/PP1PP3/RKNNQRBB w FAfa - 1 9 +epd rkn1qrbb/pp1ppp2/2p1n1p1/7p/2P2P1P/6P1/PP1PP3/RKNNQRBB w FAfa - perft 1 32 perft 2 867 perft 3 27595 @@ -6761,7 +6761,7 @@ perft 5 24485663 perft 6 688115847 id 751 -epd b1rknnrq/bpppp1p1/p6p/5p1P/6P1/4N3/PPPPPP2/BBRKN1RQ w GCgc - 1 9 +epd b1rknnrq/bpppp1p1/p6p/5p1P/6P1/4N3/PPPPPP2/BBRKN1RQ w GCgc - perft 1 33 perft 2 851 perft 3 28888 @@ -6770,7 +6770,7 @@ perft 5 26686205 perft 6 731944177 id 752 -epd brkb1nr1/pppppp2/3n2pp/3B4/1P6/4P3/PqPP1PPP/BRK1NNRQ w GBgb - 2 9 +epd brkb1nr1/pppppp2/3n2pp/3B4/1P6/4P3/PqPP1PPP/BRK1NNRQ w GBgb - perft 1 4 perft 2 98 perft 3 2965 @@ -6779,7 +6779,7 @@ perft 5 2352530 perft 6 64251468 id 753 -epd brk1nbrq/1ppppn1p/6p1/p4p2/P5P1/5R2/1PPPPP1P/BRKNNB1Q w Bgb - 0 9 +epd brk1nbrq/1ppppn1p/6p1/p4p2/P5P1/5R2/1PPPPP1P/BRKNNB1Q w Bgb - perft 1 29 perft 2 922 perft 3 27709 @@ -6788,7 +6788,7 @@ perft 5 27463717 perft 6 888881062 id 754 -epd brkn1rqb/1p1ppppp/3n4/p1p5/1P3P2/8/PNPPP1PP/BR1KNRQB w fb - 1 9 +epd brkn1rqb/1p1ppppp/3n4/p1p5/1P3P2/8/PNPPP1PP/BR1KNRQB w fb - perft 1 29 perft 2 633 perft 3 19399 @@ -6797,7 +6797,7 @@ perft 5 15076198 perft 6 396737074 id 755 -epd rb1k1nrq/pbp1pppp/1p1p1n2/8/5P2/4NN1P/PPPPP1P1/RBBK2RQ w GAga - 2 9 +epd rb1k1nrq/pbp1pppp/1p1p1n2/8/5P2/4NN1P/PPPPP1P1/RBBK2RQ w GAga - perft 1 28 perft 2 841 perft 3 24056 @@ -6806,7 +6806,7 @@ perft 5 20772996 perft 6 613798447 id 756 -epd rkbbnnrq/p1pp3p/4p1p1/1p3p2/P6P/1P6/1BPPPPP1/RK1BNNRQ w GAga - 0 9 +epd rkbbnnrq/p1pp3p/4p1p1/1p3p2/P6P/1P6/1BPPPPP1/RK1BNNRQ w GAga - perft 1 33 perft 2 957 perft 3 30668 @@ -6815,7 +6815,7 @@ perft 5 29735654 perft 6 903933626 id 757 -epd rk2nbrq/p1ppppp1/bpn5/7p/6P1/2N2P2/PPPPP1QP/RKB1NBR1 w GAga - 2 9 +epd rk2nbrq/p1ppppp1/bpn5/7p/6P1/2N2P2/PPPPP1QP/RKB1NBR1 w GAga - perft 1 24 perft 2 687 perft 3 18206 @@ -6824,7 +6824,7 @@ perft 5 15518417 perft 6 484217179 id 758 -epd rkbn1r1b/pp1pppnp/6q1/2p3p1/5P1P/4N3/PPPPP1P1/RKB1NRQB w FAfa - 1 9 +epd rkbn1r1b/pp1pppnp/6q1/2p3p1/5P1P/4N3/PPPPP1P1/RKB1NRQB w FAfa - perft 1 23 perft 2 831 perft 3 21254 @@ -6833,7 +6833,7 @@ perft 5 21126103 perft 6 744755212 id 759 -epd rbknb1rq/ppp1p1p1/3pnp1p/8/6PP/2PP4/PP2PP2/RBKNBNRQ w GAga - 0 9 +epd rbknb1rq/ppp1p1p1/3pnp1p/8/6PP/2PP4/PP2PP2/RBKNBNRQ w GAga - perft 1 31 perft 2 838 perft 3 26800 @@ -6842,7 +6842,7 @@ perft 5 24008129 perft 6 677776408 id 760 -epd rknbb1rq/p1pn1ppp/4p3/1p1p4/2P5/1P2N1P1/P2PPP1P/RKNBB1RQ w GAga - 1 9 +epd rknbb1rq/p1pn1ppp/4p3/1p1p4/2P5/1P2N1P1/P2PPP1P/RKNBB1RQ w GAga - perft 1 29 perft 2 830 perft 3 24798 @@ -6851,7 +6851,7 @@ perft 5 22243832 perft 6 660040360 id 761 -epd rk1nbbrq/pp1p1ppp/3n4/P3p3/2p4P/8/1PPPPPP1/RKNNBBRQ w GAga - 1 9 +epd rk1nbbrq/pp1p1ppp/3n4/P3p3/2p4P/8/1PPPPPP1/RKNNBBRQ w GAga - perft 1 24 perft 2 484 perft 3 12776 @@ -6860,7 +6860,7 @@ perft 5 8379748 perft 6 214004367 id 762 -epd rknnbr1b/ppp2pqp/3p4/4p1p1/7P/3P1P2/PPP1P1P1/RKNNBRQB w FAfa - 0 9 +epd rknnbr1b/ppp2pqp/3p4/4p1p1/7P/3P1P2/PPP1P1P1/RKNNBRQB w FAfa - perft 1 32 perft 2 838 perft 3 26408 @@ -6869,7 +6869,7 @@ perft 5 23472124 perft 6 699211365 id 763 -epd rb1k1rbq/ppppN1pp/2nn4/5p2/7P/8/PPPPPPP1/RBK1NRBQ w FA - 1 9 +epd rb1k1rbq/ppppN1pp/2nn4/5p2/7P/8/PPPPPPP1/RBK1NRBQ w FA - perft 1 27 perft 2 800 perft 3 22785 @@ -6878,7 +6878,7 @@ perft 5 20804424 perft 6 660917073 id 764 -epd r1nbnrbq/kppppp1p/6p1/8/p1PP1P2/4P3/PP4PP/RKNBNRBQ w FA - 1 9 +epd r1nbnrbq/kppppp1p/6p1/8/p1PP1P2/4P3/PP4PP/RKNBNRBQ w FA - perft 1 28 perft 2 757 perft 3 21198 @@ -6887,7 +6887,7 @@ perft 5 17180857 perft 6 507618340 id 765 -epd rkn1rbbq/p1pppppp/2n5/1pP5/8/1N2P3/PP1P1PPP/RK1NRBBQ w EAea - 1 9 +epd rkn1rbbq/p1pppppp/2n5/1pP5/8/1N2P3/PP1P1PPP/RK1NRBBQ w EAea - perft 1 22 perft 2 483 perft 3 11890 @@ -6896,7 +6896,7 @@ perft 5 7497674 perft 6 191130942 id 766 -epd rknnrqbb/2pppppp/8/p7/Np3P2/3P4/PPP1P1PP/RKN1RQBB w EAea - 0 9 +epd rknnrqbb/2pppppp/8/p7/Np3P2/3P4/PPP1P1PP/RKN1RQBB w EAea - perft 1 25 perft 2 536 perft 3 14456 @@ -6905,7 +6905,7 @@ perft 5 9694947 perft 6 245669668 id 767 -epd bb1rknrn/1qppppp1/1p4B1/p6N/8/2P5/PP1PPPPP/B1QRK1RN w GDgd - 1 9 +epd bb1rknrn/1qppppp1/1p4B1/p6N/8/2P5/PP1PPPPP/B1QRK1RN w GDgd - perft 1 32 perft 2 715 perft 3 22421 @@ -6914,7 +6914,7 @@ perft 5 17860156 perft 6 502410909 id 768 -epd b1rbknrn/qpp1ppp1/p6p/3p4/2P5/1P1P1P2/P3P1PP/BQRBKNRN w GCgc - 0 9 +epd b1rbknrn/qpp1ppp1/p6p/3p4/2P5/1P1P1P2/P3P1PP/BQRBKNRN w GCgc - perft 1 30 perft 2 818 perft 3 24421 @@ -6923,7 +6923,7 @@ perft 5 20981488 perft 6 611986786 id 769 -epd bqkrnbrn/1pp1pp1p/p7/1B1p2p1/4P3/7P/PPPP1PP1/BQKRN1RN w - - 0 9 +epd bqkrnbrn/1pp1pp1p/p7/1B1p2p1/4P3/7P/PPPP1PP1/BQKRN1RN w - - perft 1 28 perft 2 676 perft 3 18366 @@ -6932,7 +6932,7 @@ perft 5 13126287 perft 6 363765666 id 770 -epd bqrknrnb/1p2ppp1/p1pp3p/8/3P1P2/1PP5/P3P1PP/BQRKNRNB w FCfc - 0 9 +epd bqrknrnb/1p2ppp1/p1pp3p/8/3P1P2/1PP5/P3P1PP/BQRKNRNB w FCfc - perft 1 31 perft 2 646 perft 3 20686 @@ -6941,7 +6941,7 @@ perft 5 14984618 perft 6 349082278 id 771 -epd qbbrkn1r/pppppp1p/8/6p1/2P1Pn1P/6N1/PP1P1PP1/QBBRKNR1 w GDd - 3 9 +epd qbbrkn1r/pppppp1p/8/6p1/2P1Pn1P/6N1/PP1P1PP1/QBBRKNR1 w GDd - perft 1 20 perft 2 532 perft 3 11581 @@ -6950,7 +6950,7 @@ perft 5 7512432 perft 6 202967948 id 772 -epd 1rbbknr1/p1ppp1pp/1pq2pn1/8/3P4/P3P3/QPP2PPP/1RBBKNRN w GBgb - 3 9 +epd 1rbbknr1/p1ppp1pp/1pq2pn1/8/3P4/P3P3/QPP2PPP/1RBBKNRN w GBgb - perft 1 31 perft 2 1002 perft 3 30581 @@ -6959,7 +6959,7 @@ perft 5 30642468 perft 6 1009228283 id 773 -epd qrbkn1rn/pppp1ppp/8/6b1/P1P1Pp2/8/1P1P2PP/QRBKNBRN w GBgb - 0 9 +epd qrbkn1rn/pppp1ppp/8/6b1/P1P1Pp2/8/1P1P2PP/QRBKNBRN w GBgb - perft 1 22 perft 2 505 perft 3 12447 @@ -6968,7 +6968,7 @@ perft 5 8192621 perft 6 214730959 id 774 -epd qrbk1rnb/p2ppp1p/5n2/1pp3p1/8/7P/PPPPPPPN/QRBKR1NB w Bfb - 0 9 +epd qrbk1rnb/p2ppp1p/5n2/1pp3p1/8/7P/PPPPPPPN/QRBKR1NB w Bfb - perft 1 20 perft 2 619 perft 3 13448 @@ -6977,7 +6977,7 @@ perft 5 10571176 perft 6 369603424 id 775 -epd qbrkb1r1/ppp2ppp/3pn1n1/P3p3/4P3/3P4/1PP2PPP/QBRKBNRN w GCgc - 1 9 +epd qbrkb1r1/ppp2ppp/3pn1n1/P3p3/4P3/3P4/1PP2PPP/QBRKBNRN w GCgc - perft 1 26 perft 2 755 perft 3 20596 @@ -6986,7 +6986,7 @@ perft 5 17164382 perft 6 510878835 id 776 -epd qrkbb1r1/ppp1pnpp/3p2n1/5p2/1P3P2/2Q3N1/P1PPP1PP/1RKBB1RN w GBgb - 0 9 +epd qrkbb1r1/ppp1pnpp/3p2n1/5p2/1P3P2/2Q3N1/P1PPP1PP/1RKBB1RN w GBgb - perft 1 35 perft 2 918 perft 3 32244 @@ -6995,7 +6995,7 @@ perft 5 30933394 perft 6 867833733 id 777 -epd qrknbbrn/ppp1ppp1/8/7p/2Bp4/4PPP1/PPPP3P/QRKNB1RN w GBgb - 0 9 +epd qrknbbrn/ppp1ppp1/8/7p/2Bp4/4PPP1/PPPP3P/QRKNB1RN w GBgb - perft 1 27 perft 2 593 perft 3 16168 @@ -7004,7 +7004,7 @@ perft 5 10422676 perft 6 258348640 id 778 -epd qrk1brnb/ppppp3/4n2p/5pp1/2PP4/2N4P/PP2PPP1/QRK1BRNB w FBfb - 2 9 +epd qrk1brnb/ppppp3/4n2p/5pp1/2PP4/2N4P/PP2PPP1/QRK1BRNB w FBfb - perft 1 24 perft 2 672 perft 3 17447 @@ -7013,7 +7013,7 @@ perft 5 13765777 perft 6 414930519 id 779 -epd qbrknrb1/p2ppppp/2p3n1/8/p4P2/6PP/1PPPP3/QBRKNRBN w FCfc - 0 9 +epd qbrknrb1/p2ppppp/2p3n1/8/p4P2/6PP/1PPPP3/QBRKNRBN w FCfc - perft 1 29 perft 2 759 perft 3 23235 @@ -7022,7 +7022,7 @@ perft 5 20416668 perft 6 584870558 id 780 -epd 1rkb1rbn/p1pp1ppp/3np3/1p6/4qP2/3NB3/PPPPPRPP/QRKB3N w Bfb - 0 9 +epd 1rkb1rbn/p1pp1ppp/3np3/1p6/4qP2/3NB3/PPPPPRPP/QRKB3N w Bfb - perft 1 22 perft 2 923 perft 3 22585 @@ -7031,7 +7031,7 @@ perft 5 24049880 perft 6 957218571 id 781 -epd 1rknrbbn/p1pp1p1p/8/1p2p1p1/4qPP1/2P5/PP1PP1BP/QRKNR1BN w EBeb - 0 9 +epd 1rknrbbn/p1pp1p1p/8/1p2p1p1/4qPP1/2P5/PP1PP1BP/QRKNR1BN w EBeb - perft 1 28 perft 2 1309 perft 3 36355 @@ -7040,7 +7040,7 @@ perft 5 44576409 perft 6 1846382333 id 782 -epd qrk1rn1b/ppppp2p/4n3/3b1pp1/4P2P/5BP1/PPPP1P2/QRKNRNB1 w EBeb - 3 9 +epd qrk1rn1b/ppppp2p/4n3/3b1pp1/4P2P/5BP1/PPPP1P2/QRKNRNB1 w EBeb - perft 1 26 perft 2 839 perft 3 22189 @@ -7049,7 +7049,7 @@ perft 5 19978260 perft 6 661207281 id 783 -epd bbrqk1rn/pp1ppppp/8/2p5/2P1P3/5n1P/PPBP1PP1/B1RQKNRN w GCgc - 1 9 +epd bbrqk1rn/pp1ppppp/8/2p5/2P1P3/5n1P/PPBP1PP1/B1RQKNRN w GCgc - perft 1 3 perft 2 95 perft 3 2690 @@ -7058,7 +7058,7 @@ perft 5 2518864 perft 6 80775549 id 784 -epd brqbk2n/pppppprp/8/6p1/1P3n2/5P2/P1PPP1PP/R1QBKNRN w Gb - 2 9 +epd brqbk2n/pppppprp/8/6p1/1P3n2/5P2/P1PPP1PP/R1QBKNRN w Gb - perft 1 22 perft 2 593 perft 3 13255 @@ -7067,7 +7067,7 @@ perft 5 8922397 perft 6 253271592 id 785 -epd brqknbr1/pp3ppp/3p2n1/2p1p3/2P5/5P2/PPKPP1PP/BRQ1NBRN w gb - 0 9 +epd brqknbr1/pp3ppp/3p2n1/2p1p3/2P5/5P2/PPKPP1PP/BRQ1NBRN w gb - perft 1 21 perft 2 590 perft 3 13190 @@ -7076,7 +7076,7 @@ perft 5 9581695 perft 6 304103516 id 786 -epd 1rqknrnb/2pp1ppp/p3p3/1p6/P2P4/5bP1/1PP1PP1P/BRQKNRNB w FBfb - 0 9 +epd 1rqknrnb/2pp1ppp/p3p3/1p6/P2P4/5bP1/1PP1PP1P/BRQKNRNB w FBfb - perft 1 24 perft 2 737 perft 3 20052 @@ -7085,7 +7085,7 @@ perft 5 17948681 perft 6 536330341 id 787 -epd rbb1k1rn/p1pqpppp/6n1/1p1p4/5P2/3PP3/PPP1K1PP/RBBQ1NRN w ga - 3 9 +epd rbb1k1rn/p1pqpppp/6n1/1p1p4/5P2/3PP3/PPP1K1PP/RBBQ1NRN w ga - perft 1 24 perft 2 694 perft 3 16773 @@ -7094,7 +7094,7 @@ perft 5 13094823 perft 6 419402704 id 788 -epd rqbbknr1/1ppp2pp/p5n1/4pp2/P7/1PP5/1Q1PPPPP/R1BBKNRN w GAga - 0 9 +epd rqbbknr1/1ppp2pp/p5n1/4pp2/P7/1PP5/1Q1PPPPP/R1BBKNRN w GAga - perft 1 24 perft 2 600 perft 3 15347 @@ -7103,7 +7103,7 @@ perft 5 11029596 perft 6 308553169 id 789 -epd rqbknbrn/2pppppp/6Q1/pp6/8/2P5/PP1PPPPP/R1BKNBRN w GAga - 2 9 +epd rqbknbrn/2pppppp/6Q1/pp6/8/2P5/PP1PPPPP/R1BKNBRN w GAga - perft 1 40 perft 2 949 perft 3 34100 @@ -7112,7 +7112,7 @@ perft 5 31296485 perft 6 881529007 id 790 -epd rqbknr1b/pp1ppp2/2p2n1p/6p1/8/3P1PPP/PPP1P3/RQBKNRNB w FAfa - 0 9 +epd rqbknr1b/pp1ppp2/2p2n1p/6p1/8/3P1PPP/PPP1P3/RQBKNRNB w FAfa - perft 1 20 perft 2 560 perft 3 12275 @@ -7121,7 +7121,7 @@ perft 5 8687544 perft 6 277906201 id 791 -epd rbqkbnrn/p3pppp/1p6/3p4/P1p3P1/1P6/1QPPPP1P/RB1KBNRN w GAga - 0 9 +epd rbqkbnrn/p3pppp/1p6/3p4/P1p3P1/1P6/1QPPPP1P/RB1KBNRN w GAga - perft 1 30 perft 2 1155 perft 3 35865 @@ -7130,7 +7130,7 @@ perft 5 43092716 perft 6 1614019629 id 792 -epd rqkbb1rn/p1p1pppn/1p1p4/7p/4PP2/7P/PPPPB1P1/RQK1BNRN w GAga - 1 9 +epd rqkbb1rn/p1p1pppn/1p1p4/7p/4PP2/7P/PPPPB1P1/RQK1BNRN w GAga - perft 1 30 perft 2 701 perft 3 20804 @@ -7139,7 +7139,7 @@ perft 5 15450970 perft 6 401499189 id 793 -epd rqknbbrn/1p2pp1p/3p2p1/p1p5/P2P4/1P6/1KP1PPPP/RQ1NBBRN w ga - 0 9 +epd rqknbbrn/1p2pp1p/3p2p1/p1p5/P2P4/1P6/1KP1PPPP/RQ1NBBRN w ga - perft 1 28 perft 2 756 perft 3 21655 @@ -7148,7 +7148,7 @@ perft 5 17989811 perft 6 525585996 id 794 -epd rqknbrnb/1pp3pp/5p2/p2pp3/P7/3PPN2/1PP2PPP/RQKNBR1B w FAfa - 0 9 +epd rqknbrnb/1pp3pp/5p2/p2pp3/P7/3PPN2/1PP2PPP/RQKNBR1B w FAfa - perft 1 26 perft 2 731 perft 3 19509 @@ -7157,7 +7157,7 @@ perft 5 15209404 perft 6 439767476 id 795 -epd rbqkr1bn/p1pppp1p/1p1n4/6p1/7P/3P1PP1/PPP1P3/RBQKNRBN w FAa - 0 9 +epd rbqkr1bn/p1pppp1p/1p1n4/6p1/7P/3P1PP1/PPP1P3/RBQKNRBN w FAa - perft 1 27 perft 2 586 perft 3 16282 @@ -7166,7 +7166,7 @@ perft 5 10905865 perft 6 274364342 id 796 -epd rqk1nrb1/ppbp1ppp/4p1n1/2p5/7P/1PP5/P2PPPP1/RQKBNRBN w FAfa - 1 9 +epd rqk1nrb1/ppbp1ppp/4p1n1/2p5/7P/1PP5/P2PPPP1/RQKBNRBN w FAfa - perft 1 27 perft 2 749 perft 3 21480 @@ -7175,7 +7175,7 @@ perft 5 18084787 perft 6 520547029 id 797 -epd rqknrbbn/pp1p1ppp/4p3/2p5/3P2P1/7P/PPP1PP2/RQKNRBBN w EAa - 0 9 +epd rqknrbbn/pp1p1ppp/4p3/2p5/3P2P1/7P/PPP1PP2/RQKNRBBN w EAa - perft 1 20 perft 2 533 perft 3 11829 @@ -7184,7 +7184,7 @@ perft 5 8230417 perft 6 245871540 id 798 -epd rqknrnbb/pp1ppp1p/2p3p1/8/8/1P2P1NP/P1PP1PP1/RQKNR1BB w EAea - 0 9 +epd rqknrnbb/pp1ppp1p/2p3p1/8/8/1P2P1NP/P1PP1PP1/RQKNR1BB w EAea - perft 1 22 perft 2 633 perft 3 14480 @@ -7193,7 +7193,7 @@ perft 5 10827868 perft 6 343525739 id 799 -epd 1brkq1rn/2pppppp/1p2n3/p2bN3/8/7P/PPPPPPP1/BBRKQ1RN w GCgc - 2 9 +epd 1brkq1rn/2pppppp/1p2n3/p2bN3/8/7P/PPPPPPP1/BBRKQ1RN w GCgc - perft 1 27 perft 2 748 perft 3 20134 @@ -7202,7 +7202,7 @@ perft 5 16010135 perft 6 475206624 id 800 -epd brkbqnrn/2pp1ppp/8/1p2p3/Pp2N3/8/2PPPPPP/BRKBQNR1 w GBgb - 0 9 +epd brkbqnrn/2pp1ppp/8/1p2p3/Pp2N3/8/2PPPPPP/BRKBQNR1 w GBgb - perft 1 30 perft 2 827 perft 3 25308 @@ -7211,7 +7211,7 @@ perft 5 23746165 perft 6 751690068 id 801 -epd brk1nbrn/pp1ppppp/2p5/7P/5P2/q2P4/PPP1P1P1/BRKQNBRN w GBgb - 1 9 +epd brk1nbrn/pp1ppppp/2p5/7P/5P2/q2P4/PPP1P1P1/BRKQNBRN w GBgb - perft 1 15 perft 2 471 perft 3 8716 @@ -7220,7 +7220,7 @@ perft 5 5960901 perft 6 190316951 id 802 -epd brkqnrnb/1p1pp1p1/p4p2/2p4p/8/P2PP3/1PP1QPPP/BRK1NRNB w FBfb - 0 9 +epd brkqnrnb/1p1pp1p1/p4p2/2p4p/8/P2PP3/1PP1QPPP/BRK1NRNB w FBfb - perft 1 24 perft 2 479 perft 3 12584 @@ -7229,7 +7229,7 @@ perft 5 7830230 perft 6 190419716 id 803 -epd rbbkqnrn/2ppp2p/pp3p2/6p1/P6P/8/RPPPPPP1/1BBKQNRN w Gga - 0 9 +epd rbbkqnrn/2ppp2p/pp3p2/6p1/P6P/8/RPPPPPP1/1BBKQNRN w Gga - perft 1 21 perft 2 523 perft 3 12125 @@ -7238,7 +7238,7 @@ perft 5 8322614 perft 6 242240658 id 804 -epd rkbbqr1n/1ppppppn/7p/p7/4P3/2P2P2/PP1PB1PP/RKB1QNRN w GAa - 3 9 +epd rkbbqr1n/1ppppppn/7p/p7/4P3/2P2P2/PP1PB1PP/RKB1QNRN w GAa - perft 1 27 perft 2 563 perft 3 16026 @@ -7247,7 +7247,7 @@ perft 5 11105151 perft 6 283211800 id 805 -epd rkbqnbrn/ppppp3/8/5ppp/2P3P1/7P/PPQPPP2/RKB1NBRN w GAga - 0 9 +epd rkbqnbrn/ppppp3/8/5ppp/2P3P1/7P/PPQPPP2/RKB1NBRN w GAga - perft 1 28 perft 2 639 perft 3 19250 @@ -7256,7 +7256,7 @@ perft 5 14872172 perft 6 384663405 id 806 -epd rkb1nrnb/pppp1pp1/5q1p/8/P3p3/4R1P1/1PPPPP1P/1KBQNRNB w Ffa - 0 9 +epd rkb1nrnb/pppp1pp1/5q1p/8/P3p3/4R1P1/1PPPPP1P/1KBQNRNB w Ffa - perft 1 28 perft 2 873 perft 3 23690 @@ -7265,7 +7265,7 @@ perft 5 20209424 perft 6 625281937 id 807 -epd rbkqb1rn/1p1ppppp/4n3/p1p5/8/3PBP2/PPP1P1PP/RBKQ1NRN w GAga - 0 9 +epd rbkqb1rn/1p1ppppp/4n3/p1p5/8/3PBP2/PPP1P1PP/RBKQ1NRN w GAga - perft 1 26 perft 2 798 perft 3 21416 @@ -7274,7 +7274,7 @@ perft 5 18475618 perft 6 591681956 id 808 -epd rk1qbnrn/1p1ppppp/1b6/p1p5/P7/2P3NP/1P1PPPP1/RKQBB1RN w GAga - 0 9 +epd rk1qbnrn/1p1ppppp/1b6/p1p5/P7/2P3NP/1P1PPPP1/RKQBB1RN w GAga - perft 1 22 perft 2 506 perft 3 12313 @@ -7283,7 +7283,7 @@ perft 5 7891676 perft 6 205739580 id 809 -epd rk1nbbrn/ppp1ppp1/8/3p3p/1P1P2q1/5PB1/P1P1P1PP/RKQN1BRN w GAga - 1 9 +epd rk1nbbrn/ppp1ppp1/8/3p3p/1P1P2q1/5PB1/P1P1P1PP/RKQN1BRN w GAga - perft 1 31 perft 2 956 perft 3 29219 @@ -7292,7 +7292,7 @@ perft 5 27827461 perft 6 876341492 id 810 -epd rkqnbr1b/pp1pppp1/7p/2p2n2/P2P4/7N/RPP1PPPP/1KQNBR1B w Ffa - 0 9 +epd rkqnbr1b/pp1pppp1/7p/2p2n2/P2P4/7N/RPP1PPPP/1KQNBR1B w Ffa - perft 1 31 perft 2 750 perft 3 24267 @@ -7301,7 +7301,7 @@ perft 5 21639104 perft 6 617064197 id 811 -epd rbkq1rbn/2p1pppp/pp3n2/3p4/5P2/3N2N1/PPPPP1PP/RBKQR1B1 w Afa - 2 9 +epd rbkq1rbn/2p1pppp/pp3n2/3p4/5P2/3N2N1/PPPPP1PP/RBKQR1B1 w Afa - perft 1 26 perft 2 647 perft 3 18027 @@ -7310,7 +7310,7 @@ perft 5 13643783 perft 6 369702807 id 812 -epd rkqbr1bn/p2ppppp/1pp2n2/8/5P2/3P1N2/PPP1PRPP/RKQB2BN w Aa - 3 9 +epd rkqbr1bn/p2ppppp/1pp2n2/8/5P2/3P1N2/PPP1PRPP/RKQB2BN w Aa - perft 1 24 perft 2 574 perft 3 14593 @@ -7319,7 +7319,7 @@ perft 5 10066892 perft 6 271121237 id 813 -epd rk1qrbbn/p1ppp1pp/1p2n3/5p2/1P6/K3N3/P1PPPPPP/R1Q1RBBN w ea - 0 9 +epd rk1qrbbn/p1ppp1pp/1p2n3/5p2/1P6/K3N3/P1PPPPPP/R1Q1RBBN w ea - perft 1 25 perft 2 548 perft 3 14069 @@ -7328,7 +7328,7 @@ perft 5 9043111 perft 6 235545764 id 814 -epd rkqnrnbb/pp1pp3/2p5/5ppp/8/PP4NP/2PPPPP1/RKQNR1BB w EAea - 0 9 +epd rkqnrnbb/pp1pp3/2p5/5ppp/8/PP4NP/2PPPPP1/RKQNR1BB w EAea - perft 1 23 perft 2 727 perft 3 18228 @@ -7337,7 +7337,7 @@ perft 5 15078056 perft 6 471296844 id 815 -epd bbrknq1r/ppppppp1/8/7p/5n2/3P4/PPP1PNPP/BBKRNQR1 w c - 0 9 +epd bbrknq1r/ppppppp1/8/7p/5n2/3P4/PPP1PNPP/BBKRNQR1 w c - perft 1 21 perft 2 610 perft 3 13300 @@ -7346,7 +7346,7 @@ perft 5 9605845 perft 6 293532398 id 816 -epd brkbnqr1/2pppnpp/pp3p2/8/4PPPP/8/PPPP4/BRKBNQRN w GBgb - 1 9 +epd brkbnqr1/2pppnpp/pp3p2/8/4PPPP/8/PPPP4/BRKBNQRN w GBgb - perft 1 30 perft 2 757 perft 3 23908 @@ -7355,7 +7355,7 @@ perft 5 20360394 perft 6 548380577 id 817 -epd brk1qb1n/ppppppr1/2n3pp/8/2P3P1/2N5/PP1PPP1P/BR1KQBRN w b - 1 9 +epd brk1qb1n/ppppppr1/2n3pp/8/2P3P1/2N5/PP1PPP1P/BR1KQBRN w b - perft 1 26 perft 2 570 perft 3 15537 @@ -7364,7 +7364,7 @@ perft 5 10081351 perft 6 242864559 id 818 -epd brknq1nb/pp2prpp/8/2pP1p2/6P1/2N5/PPPP1P1P/BRK1QRNB w FBb - 1 9 +epd brknq1nb/pp2prpp/8/2pP1p2/6P1/2N5/PPPP1P1P/BRK1QRNB w FBb - perft 1 33 perft 2 830 perft 3 27897 @@ -7373,7 +7373,7 @@ perft 5 26262884 perft 6 765831403 id 819 -epd rbbk1qrn/ppp1p1pp/5p2/3p1n2/7N/P7/1PPPPPPP/RBB1KQRN w ga - 0 9 +epd rbbk1qrn/ppp1p1pp/5p2/3p1n2/7N/P7/1PPPPPPP/RBB1KQRN w ga - perft 1 21 perft 2 562 perft 3 13060 @@ -7382,7 +7382,7 @@ perft 5 9520963 perft 6 290579255 id 820 -epd rk1b1qrn/ppp1pppp/5n2/3pN3/P6P/7b/1PPPPPP1/RKBB1QRN w GAga - 4 9 +epd rk1b1qrn/ppp1pppp/5n2/3pN3/P6P/7b/1PPPPPP1/RKBB1QRN w GAga - perft 1 28 perft 2 677 perft 3 19235 @@ -7391,7 +7391,7 @@ perft 5 14354779 perft 6 383207197 id 821 -epd rkbnqbrn/pp1ppp1p/2p5/6p1/P7/4P3/KPPPQPPP/R1BN1BRN w - - 3 9 +epd rkbnqbrn/pp1ppp1p/2p5/6p1/P7/4P3/KPPPQPPP/R1BN1BRN w - - perft 1 28 perft 2 585 perft 3 17443 @@ -7400,7 +7400,7 @@ perft 5 12574541 perft 6 310495538 id 822 -epd rk1nqrnb/pbpppp2/1p4p1/7p/P7/5NP1/1PPPPPBP/RKBNQR2 w FAfa - 2 9 +epd rk1nqrnb/pbpppp2/1p4p1/7p/P7/5NP1/1PPPPPBP/RKBNQR2 w FAfa - perft 1 26 perft 2 774 perft 3 21626 @@ -7409,7 +7409,7 @@ perft 5 19093408 perft 6 576325868 id 823 -epd rbknb1rn/p1pp2pp/1p6/4pp2/1q3P1B/2N5/PPPPPNPP/RBK2QR1 w GAga - 2 9 +epd rbknb1rn/p1pp2pp/1p6/4pp2/1q3P1B/2N5/PPPPPNPP/RBK2QR1 w GAga - perft 1 31 perft 2 1206 perft 3 36940 @@ -7418,7 +7418,7 @@ perft 5 42849564 perft 6 1555711209 id 824 -epd rk1bbqrn/pp1pp1pp/3n4/5p2/3p4/1PP5/PK2PPPP/R1NBBQRN w ga - 0 9 +epd rk1bbqrn/pp1pp1pp/3n4/5p2/3p4/1PP5/PK2PPPP/R1NBBQRN w ga - perft 1 21 perft 2 629 perft 3 14059 @@ -7427,7 +7427,7 @@ perft 5 10587910 perft 6 332632033 id 825 -epd rknqbbr1/p1pp1pp1/1p4n1/4p2p/4P1P1/6RB/PPPP1P1P/RKNQB2N w Aga - 0 9 +epd rknqbbr1/p1pp1pp1/1p4n1/4p2p/4P1P1/6RB/PPPP1P1P/RKNQB2N w Aga - perft 1 27 perft 2 753 perft 3 20918 @@ -7436,7 +7436,7 @@ perft 5 17318772 perft 6 507563675 id 826 -epd rknqbr1b/pppp1ppp/4p2n/8/1P3P2/4P3/P1PPN1PP/RKNQBR1B w FAfa - 2 9 +epd rknqbr1b/pppp1ppp/4p2n/8/1P3P2/4P3/P1PPN1PP/RKNQBR1B w FAfa - perft 1 26 perft 2 623 perft 3 17177 @@ -7445,7 +7445,7 @@ perft 5 13389799 perft 6 383508368 id 827 -epd r2kqrbn/bppppppp/2n5/p4B2/5P2/2P5/PP1PP1PP/1RKNQRBN w F - 2 9 +epd r2kqrbn/bppppppp/2n5/p4B2/5P2/2P5/PP1PP1PP/1RKNQRBN w F - perft 1 39 perft 2 1026 perft 3 37800 @@ -7454,7 +7454,7 @@ perft 5 35946987 perft 6 992756232 id 828 -epd rk1bqrb1/ppppppp1/1n6/7p/2P2P1n/4P1Q1/PP1P2PP/RKNB1RBN w FAfa - 0 9 +epd rk1bqrb1/ppppppp1/1n6/7p/2P2P1n/4P1Q1/PP1P2PP/RKNB1RBN w FAfa - perft 1 35 perft 2 760 perft 3 25817 @@ -7463,7 +7463,7 @@ perft 5 21014787 perft 6 536852043 id 829 -epd rkq1rb1n/ppppp1pp/1n6/5p2/PPb2P2/8/1KPPP1PP/R1NQRBBN w ea - 1 9 +epd rkq1rb1n/ppppp1pp/1n6/5p2/PPb2P2/8/1KPPP1PP/R1NQRBBN w ea - perft 1 27 perft 2 754 perft 3 21009 @@ -7472,7 +7472,7 @@ perft 5 16461795 perft 6 448313956 id 830 -epd rknqr2b/pppnp1pp/3p4/3b1p2/8/1N1P2N1/PPP1PPPP/RKQ1R1BB w EAea - 1 9 +epd rknqr2b/pppnp1pp/3p4/3b1p2/8/1N1P2N1/PPP1PPPP/RKQ1R1BB w EAea - perft 1 27 perft 2 803 perft 3 23708 @@ -7481,7 +7481,7 @@ perft 5 21875031 perft 6 654754840 id 831 -epd bbrknrqn/ppppp1pB/8/2P2p1p/8/5N2/PP1PPPPP/B1RK1RQN w FCfc - 0 9 +epd bbrknrqn/ppppp1pB/8/2P2p1p/8/5N2/PP1PPPPP/B1RK1RQN w FCfc - perft 1 30 perft 2 799 perft 3 23923 @@ -7490,7 +7490,7 @@ perft 5 20532790 perft 6 603059376 id 832 -epd brkbnrq1/1pppp1p1/6np/p4p2/4P3/1PP5/P1KP1PPP/BR1BNRQN w fb - 1 9 +epd brkbnrq1/1pppp1p1/6np/p4p2/4P3/1PP5/P1KP1PPP/BR1BNRQN w fb - perft 1 27 perft 2 726 perft 3 19329 @@ -7499,7 +7499,7 @@ perft 5 15156662 perft 6 457601127 id 833 -epd brknrbq1/1p1p1ppp/p3p1n1/2p5/8/1P1BPP2/P1PP2PP/BRKNR1QN w EBeb - 0 9 +epd brknrbq1/1p1p1ppp/p3p1n1/2p5/8/1P1BPP2/P1PP2PP/BRKNR1QN w EBeb - perft 1 36 perft 2 786 perft 3 27868 @@ -7508,7 +7508,7 @@ perft 5 22852433 perft 6 577223409 id 834 -epd brknrqnb/p2ppp1p/2p5/1p6/3P2p1/P1P1N3/1P2PPPP/BRK1RQNB w EBeb - 0 9 +epd brknrqnb/p2ppp1p/2p5/1p6/3P2p1/P1P1N3/1P2PPPP/BRK1RQNB w EBeb - perft 1 23 perft 2 649 perft 3 15169 @@ -7517,7 +7517,7 @@ perft 5 10687843 perft 6 320881984 id 835 -epd rbbk1rqn/1ppppppp/3n4/p7/2P5/3N4/PP1PPPPP/RBB1KRQN w fa - 1 9 +epd rbbk1rqn/1ppppppp/3n4/p7/2P5/3N4/PP1PPPPP/RBB1KRQN w fa - perft 1 20 perft 2 478 perft 3 11094 @@ -7526,7 +7526,7 @@ perft 5 7094988 perft 6 185488058 id 836 -epd rkbbnrqn/p2p1ppp/1p2p3/8/P1p1P3/1BP5/1P1P1PPP/RKB1NRQN w FAfa - 0 9 +epd rkbbnrqn/p2p1ppp/1p2p3/8/P1p1P3/1BP5/1P1P1PPP/RKB1NRQN w FAfa - perft 1 22 perft 2 570 perft 3 13295 @@ -7535,7 +7535,7 @@ perft 5 8671852 perft 6 229898448 id 837 -epd rkb1rb1n/ppppppqp/8/2n3p1/2P1P1P1/8/PP1P1P1P/RKBNRBQN w EAea - 1 9 +epd rkb1rb1n/ppppppqp/8/2n3p1/2P1P1P1/8/PP1P1P1P/RKBNRBQN w EAea - perft 1 23 perft 2 663 perft 3 16212 @@ -7544,7 +7544,7 @@ perft 5 12900485 perft 6 404944553 id 838 -epd rkb1rqnb/pppp3p/2n3p1/4pp2/P2P3P/2P5/1P2PPP1/RKBNRQNB w EAea - 0 9 +epd rkb1rqnb/pppp3p/2n3p1/4pp2/P2P3P/2P5/1P2PPP1/RKBNRQNB w EAea - perft 1 25 perft 2 845 perft 3 22188 @@ -7553,7 +7553,7 @@ perft 5 20276176 perft 6 683290790 id 839 -epd rbk1brqn/ppp1pppp/8/3p4/7P/1P4P1/2PPPP2/RBKNBRQN w FAfa - 0 9 +epd rbk1brqn/ppp1pppp/8/3p4/7P/1P4P1/2PPPP2/RBKNBRQN w FAfa - perft 1 24 perft 2 526 perft 3 13862 @@ -7562,7 +7562,7 @@ perft 5 9054028 perft 6 222704171 id 840 -epd rknbbrqn/pp3pp1/4p3/2pp3p/2P5/8/PPBPPPPP/RKN1BRQN w FAfa - 0 9 +epd rknbbrqn/pp3pp1/4p3/2pp3p/2P5/8/PPBPPPPP/RKN1BRQN w FAfa - perft 1 26 perft 2 756 perft 3 19280 @@ -7571,7 +7571,7 @@ perft 5 14697705 perft 6 433719427 id 841 -epd 1knrbbqn/rp1p1ppp/p3p3/2p5/8/5P1P/PPPPP1P1/RKNRBBQN w DAd - 0 9 +epd 1knrbbqn/rp1p1ppp/p3p3/2p5/8/5P1P/PPPPP1P1/RKNRBBQN w DAd - perft 1 26 perft 2 539 perft 3 15194 @@ -7580,7 +7580,7 @@ perft 5 10223443 perft 6 248715580 id 842 -epd rknr1qnb/ppp1p1pp/3p2b1/8/4p3/1P3P1P/P1PP2P1/RKNRBQNB w DAda - 0 9 +epd rknr1qnb/ppp1p1pp/3p2b1/8/4p3/1P3P1P/P1PP2P1/RKNRBQNB w DAda - perft 1 25 perft 2 701 perft 3 18969 @@ -7589,7 +7589,7 @@ perft 5 16047041 perft 6 496340789 id 843 -epd rbk1r1bn/ppppp1pp/4n3/5p2/1P3P2/4N2P/PqPPP1P1/RBK1RQBN w EAea - 1 9 +epd rbk1r1bn/ppppp1pp/4n3/5p2/1P3P2/4N2P/PqPPP1P1/RBK1RQBN w EAea - perft 1 2 perft 2 60 perft 3 1319 @@ -7598,7 +7598,7 @@ perft 5 1017864 perft 6 33183408 id 844 -epd r1nbrqbn/k1ppp1pp/1p6/p4p2/2P5/6PQ/PP1PPP1P/RKNBR1BN w EA - 0 9 +epd r1nbrqbn/k1ppp1pp/1p6/p4p2/2P5/6PQ/PP1PPP1P/RKNBR1BN w EA - perft 1 27 perft 2 699 perft 3 20436 @@ -7607,7 +7607,7 @@ perft 5 17192121 perft 6 499247248 id 845 -epd rknrqbbn/1pp1pp2/p5p1/3p3p/6P1/PN5P/1PPPPP2/RK1RQBBN w DAda - 0 9 +epd rknrqbbn/1pp1pp2/p5p1/3p3p/6P1/PN5P/1PPPPP2/RK1RQBBN w DAda - perft 1 23 perft 2 611 perft 3 15515 @@ -7616,7 +7616,7 @@ perft 5 11917036 perft 6 352885930 id 846 -epd rknrqn1b/p1pp1ppb/8/1p2p1Qp/3P4/3N4/PPP1PPPP/RK1R1NBB w DAda - 0 9 +epd rknrqn1b/p1pp1ppb/8/1p2p1Qp/3P4/3N4/PPP1PPPP/RK1R1NBB w DAda - perft 1 45 perft 2 1170 perft 3 48283 @@ -7625,7 +7625,7 @@ perft 5 52213677 perft 6 1500007485 id 847 -epd bbkrnrnq/p2p1ppp/2p1p3/1p6/1P2Q3/6P1/P1PPPP1P/BBKRNRN1 w - - 0 9 +epd bbkrnrnq/p2p1ppp/2p1p3/1p6/1P2Q3/6P1/P1PPPP1P/BBKRNRN1 w - - perft 1 41 perft 2 1035 perft 3 39895 @@ -7634,7 +7634,7 @@ perft 5 38555608 perft 6 1037686769 id 848 -epd brkbnr2/1ppppp1p/7n/p5N1/P2q4/8/1PPPPPPP/BRKBNRQ1 w FBfb - 1 9 +epd brkbnr2/1ppppp1p/7n/p5N1/P2q4/8/1PPPPPPP/BRKBNRQ1 w FBfb - perft 1 22 perft 2 869 perft 3 19234 @@ -7643,7 +7643,7 @@ perft 5 16453359 perft 6 567287944 id 849 -epd brknrbnq/p1ppppp1/1p6/7p/2PP4/5P2/PPK1P1PP/BR1NRBNQ w eb - 1 9 +epd brknrbnq/p1ppppp1/1p6/7p/2PP4/5P2/PPK1P1PP/BR1NRBNQ w eb - perft 1 23 perft 2 641 perft 3 14748 @@ -7652,7 +7652,7 @@ perft 5 10192718 perft 6 302864305 id 850 -epd brk1r1qb/pp1ppnpp/2p2pn1/8/6N1/2N3P1/PPPPPP1P/BRK1R1QB w EBeb - 3 9 +epd brk1r1qb/pp1ppnpp/2p2pn1/8/6N1/2N3P1/PPPPPP1P/BRK1R1QB w EBeb - perft 1 32 perft 2 863 perft 3 28379 @@ -7661,7 +7661,7 @@ perft 5 25848794 perft 6 720443112 id 851 -epd rbbk1rnq/pppp1pp1/4p2p/8/3P2n1/4BN1P/PPP1PPP1/RB1K1RNQ w FAfa - 3 9 +epd rbbk1rnq/pppp1pp1/4p2p/8/3P2n1/4BN1P/PPP1PPP1/RB1K1RNQ w FAfa - perft 1 26 perft 2 628 perft 3 16151 @@ -7670,7 +7670,7 @@ perft 5 11237919 perft 6 300314373 id 852 -epd rkbbnr1q/p1pppppp/5n2/1p5B/PP6/4P3/2PP1PPP/RKB1NRNQ w FAfa - 0 9 +epd rkbbnr1q/p1pppppp/5n2/1p5B/PP6/4P3/2PP1PPP/RKB1NRNQ w FAfa - perft 1 30 perft 2 692 perft 3 21036 @@ -7679,7 +7679,7 @@ perft 5 16025428 perft 6 420887328 id 853 -epd rkb1rbnq/1pppp1pp/5p2/p7/5n1P/1PN3P1/P1PPPP2/RKB1RBNQ w EAea - 0 9 +epd rkb1rbnq/1pppp1pp/5p2/p7/5n1P/1PN3P1/P1PPPP2/RKB1RBNQ w EAea - perft 1 32 perft 2 825 perft 3 27130 @@ -7688,7 +7688,7 @@ perft 5 23593363 perft 6 622249676 id 854 -epd rkbnrnqb/1ppp1p1p/p5p1/4p3/4P3/2N2P2/PPPP2PP/RKBR1NQB w Aea - 0 9 +epd rkbnrnqb/1ppp1p1p/p5p1/4p3/4P3/2N2P2/PPPP2PP/RKBR1NQB w Aea - perft 1 24 perft 2 487 perft 3 13300 @@ -7697,7 +7697,7 @@ perft 5 8782713 perft 6 215787079 id 855 -epd rbknbr1q/pppp2pp/4p3/5p1n/1P2P2N/8/P1PP1PPP/RBKNBR1Q w FAfa - 0 9 +epd rbknbr1q/pppp2pp/4p3/5p1n/1P2P2N/8/P1PP1PPP/RBKNBR1Q w FAfa - perft 1 23 perft 2 571 perft 3 13799 @@ -7706,7 +7706,7 @@ perft 5 9224232 perft 6 257288920 id 856 -epd rknbb1nq/pppppr2/5pp1/7p/8/1N4P1/PPPPPP1P/RK1BBRNQ w FAa - 2 9 +epd rknbb1nq/pppppr2/5pp1/7p/8/1N4P1/PPPPPP1P/RK1BBRNQ w FAa - perft 1 26 perft 2 548 perft 3 15618 @@ -7715,7 +7715,7 @@ perft 5 10587626 perft 6 253006082 id 857 -epd rknr1bnq/p2pp1pp/1p3p2/2p4b/6PP/2P2N2/PP1PPP2/RKNRBB1Q w DAda - 1 9 +epd rknr1bnq/p2pp1pp/1p3p2/2p4b/6PP/2P2N2/PP1PPP2/RKNRBB1Q w DAda - perft 1 25 perft 2 502 perft 3 13150 @@ -7724,7 +7724,7 @@ perft 5 7824941 perft 6 175766730 id 858 -epd rknrb1qb/ppp1pppp/3p4/8/4P1nP/2P5/PPKP1PP1/R1NRBNQB w da - 1 9 +epd rknrb1qb/ppp1pppp/3p4/8/4P1nP/2P5/PPKP1PP1/R1NRBNQB w da - perft 1 23 perft 2 643 perft 3 14849 @@ -7733,7 +7733,7 @@ perft 5 10507328 perft 6 312096061 id 859 -epd rbk1rnbq/pppp1npp/4p3/5p2/4P1P1/7P/PPPP1P1N/RBKNR1BQ w EAea - 1 9 +epd rbk1rnbq/pppp1npp/4p3/5p2/4P1P1/7P/PPPP1P1N/RBKNR1BQ w EAea - perft 1 24 perft 2 591 perft 3 15178 @@ -7742,7 +7742,7 @@ perft 5 10251465 perft 6 263574861 id 860 -epd rknbrnb1/p1pppp1p/1p6/3N2p1/P3q1P1/8/1PPPPP1P/RKNBR1BQ w EAea - 1 9 +epd rknbrnb1/p1pppp1p/1p6/3N2p1/P3q1P1/8/1PPPPP1P/RKNBR1BQ w EAea - perft 1 28 perft 2 948 perft 3 27343 @@ -7751,7 +7751,7 @@ perft 5 26241141 perft 6 812343987 id 861 -epd rknrn1b1/ppppppqp/8/6p1/2P5/2P1BP2/PP2P1PP/RKNRNB1Q w DAda - 1 9 +epd rknrn1b1/ppppppqp/8/6p1/2P5/2P1BP2/PP2P1PP/RKNRNB1Q w DAda - perft 1 31 perft 2 807 perft 3 24360 @@ -7760,7 +7760,7 @@ perft 5 20455205 perft 6 588518645 id 862 -epd 1k1rnqbb/npppppp1/r7/p2B3p/5P2/1N4P1/PPPPP2P/RK1RNQB1 w DAd - 0 9 +epd 1k1rnqbb/npppppp1/r7/p2B3p/5P2/1N4P1/PPPPP2P/RK1RNQB1 w DAd - perft 1 40 perft 2 1122 perft 3 44297 @@ -7769,7 +7769,7 @@ perft 5 48711073 perft 6 1412437357 id 863 -epd bbqr1rkn/pp1ppppp/8/2p5/1P2P1n1/7N/P1PP1P1P/BBQRKR1N w FD - 0 9 +epd bbqr1rkn/pp1ppppp/8/2p5/1P2P1n1/7N/P1PP1P1P/BBQRKR1N w FD - perft 1 26 perft 2 841 perft 3 22986 @@ -7778,7 +7778,7 @@ perft 5 21328001 perft 6 705170410 id 864 -epd bqkr1rnn/1ppp1ppp/p4b2/4p3/P7/3PP2N/1PP2PPP/BQRBKR1N w FC - 3 9 +epd bqkr1rnn/1ppp1ppp/p4b2/4p3/P7/3PP2N/1PP2PPP/BQRBKR1N w FC - perft 1 24 perft 2 500 perft 3 12802 @@ -7787,7 +7787,7 @@ perft 5 7928916 perft 6 197806842 id 865 -epd bqrkrbnn/1pp1ppp1/8/p6p/3p4/P3P2P/QPPP1PP1/B1RKRBNN w ECec - 0 9 +epd bqrkrbnn/1pp1ppp1/8/p6p/3p4/P3P2P/QPPP1PP1/B1RKRBNN w ECec - perft 1 31 perft 2 592 perft 3 18585 @@ -7796,7 +7796,7 @@ perft 5 12607528 perft 6 298629240 id 866 -epd bqkrrnnb/2p1pppp/p7/1P1p4/8/2R3P1/PP1PPP1P/BQ1KRNNB w E - 0 9 +epd bqkrrnnb/2p1pppp/p7/1P1p4/8/2R3P1/PP1PPP1P/BQ1KRNNB w E - perft 1 42 perft 2 1124 perft 3 45187 @@ -7805,7 +7805,7 @@ perft 5 50052573 perft 6 1483524894 id 867 -epd qbbrkrn1/p1pppn1p/8/1p3Pp1/2P5/8/PP1PPP1P/QBBRKRNN w FDfd - 0 9 +epd qbbrkrn1/p1pppn1p/8/1p3Pp1/2P5/8/PP1PPP1P/QBBRKRNN w FDfd - perft 1 21 perft 2 577 perft 3 13244 @@ -7814,7 +7814,7 @@ perft 5 9683808 perft 6 300294295 id 868 -epd qrbbkrnn/pp1p2pp/4p3/5p2/2p2P1P/2P5/PP1PP1P1/QRBBKRNN w FBfb - 0 9 +epd qrbbkrnn/pp1p2pp/4p3/5p2/2p2P1P/2P5/PP1PP1P1/QRBBKRNN w FBfb - perft 1 21 perft 2 571 perft 3 12736 @@ -7823,7 +7823,7 @@ perft 5 8239872 perft 6 228837930 id 869 -epd qrbkrbn1/1pp1pppp/p2p4/8/5PPn/2P5/PP1PP3/QRBKRBNN w EBeb - 0 9 +epd qrbkrbn1/1pp1pppp/p2p4/8/5PPn/2P5/PP1PP3/QRBKRBNN w EBeb - perft 1 18 perft 2 466 perft 3 9443 @@ -7832,7 +7832,7 @@ perft 5 5679073 perft 6 162883949 id 870 -epd qrb1rnnb/pp1p1ppp/2pk4/4p3/1P2P3/1R6/P1PP1PPP/Q1BKRNNB w E - 4 9 +epd qrb1rnnb/pp1p1ppp/2pk4/4p3/1P2P3/1R6/P1PP1PPP/Q1BKRNNB w E - perft 1 37 perft 2 760 perft 3 26863 @@ -7841,7 +7841,7 @@ perft 5 19486022 perft 6 421740856 id 871 -epd qbrkbrn1/p1pppp1p/6n1/1p4p1/1P6/5P2/P1PPPBPP/QBRK1RNN w FCfc - 1 9 +epd qbrkbrn1/p1pppp1p/6n1/1p4p1/1P6/5P2/P1PPPBPP/QBRK1RNN w FCfc - perft 1 33 perft 2 824 perft 3 27385 @@ -7850,7 +7850,7 @@ perft 5 25176664 perft 6 734656217 id 872 -epd qrkbbr2/2pppppp/5nn1/pp1Q4/P7/3P4/1PP1PPPP/1RKBBRNN w FBfb - 0 9 +epd qrkbbr2/2pppppp/5nn1/pp1Q4/P7/3P4/1PP1PPPP/1RKBBRNN w FBfb - perft 1 42 perft 2 1147 perft 3 44012 @@ -7859,7 +7859,7 @@ perft 5 48216013 perft 6 1522548864 id 873 -epd qrkrbbnn/pp2pp2/2pp2pp/1B6/P7/4P3/1PPP1PPP/QRKRB1NN w DBdb - 0 9 +epd qrkrbbnn/pp2pp2/2pp2pp/1B6/P7/4P3/1PPP1PPP/QRKRB1NN w DBdb - perft 1 26 perft 2 464 perft 3 12653 @@ -7868,7 +7868,7 @@ perft 5 6928220 perft 6 142507795 id 874 -epd qrkrbnnb/p1pp1pp1/1p5p/4p3/1P6/6PN/PKPPPP1P/QR1RBN1B w db - 0 9 +epd qrkrbnnb/p1pp1pp1/1p5p/4p3/1P6/6PN/PKPPPP1P/QR1RBN1B w db - perft 1 29 perft 2 705 perft 3 20000 @@ -7877,7 +7877,7 @@ perft 5 15055365 perft 6 419552571 id 875 -epd qbrkr1bn/p1p1pp1p/1p1p2n1/6p1/3P1P2/4P3/PPP3PP/QBKRRNBN w ec - 2 9 +epd qbrkr1bn/p1p1pp1p/1p1p2n1/6p1/3P1P2/4P3/PPP3PP/QBKRRNBN w ec - perft 1 23 perft 2 613 perft 3 14835 @@ -7886,7 +7886,7 @@ perft 5 10747407 perft 6 323905533 id 876 -epd qrk1rnb1/p1pp1ppp/1p2Bbn1/8/4P3/6P1/PPPP1P1P/QRK1RNBN w EBeb - 1 9 +epd qrk1rnb1/p1pp1ppp/1p2Bbn1/8/4P3/6P1/PPPP1P1P/QRK1RNBN w EBeb - perft 1 28 perft 2 927 perft 3 24887 @@ -7895,7 +7895,7 @@ perft 5 23063284 perft 6 807913585 id 877 -epd 1qkrnbbn/1rpppppp/pp6/5N2/P4P2/8/1PPPP1PP/QRKRNBB1 w DBd - 3 9 +epd 1qkrnbbn/1rpppppp/pp6/5N2/P4P2/8/1PPPP1PP/QRKRNBB1 w DBd - perft 1 30 perft 2 542 perft 3 16646 @@ -7904,7 +7904,7 @@ perft 5 10976745 perft 6 251694423 id 878 -epd qrkr2bb/pppppppp/8/1n2n3/1N5P/1P6/P1PPPPP1/QRKR1NBB w DBdb - 1 9 +epd qrkr2bb/pppppppp/8/1n2n3/1N5P/1P6/P1PPPPP1/QRKR1NBB w DBdb - perft 1 28 perft 2 719 perft 3 21048 @@ -7913,7 +7913,7 @@ perft 5 17351761 perft 6 479400272 id 879 -epd bbrqkrnn/3ppppp/8/ppp5/6P1/4P2N/PPPPKP1P/BBRQ1R1N w fc - 0 9 +epd bbrqkrnn/3ppppp/8/ppp5/6P1/4P2N/PPPPKP1P/BBRQ1R1N w fc - perft 1 21 perft 2 704 perft 3 16119 @@ -7922,7 +7922,7 @@ perft 5 13676371 perft 6 470796854 id 880 -epd brqbkrnn/1pp2p1p/3pp1p1/p5N1/8/1P6/P1PPPPPP/BRQBK1RN w Bfb - 0 9 +epd brqbkrnn/1pp2p1p/3pp1p1/p5N1/8/1P6/P1PPPPPP/BRQBK1RN w Bfb - perft 1 34 perft 2 688 perft 3 22827 @@ -7931,7 +7931,7 @@ perft 5 16639723 perft 6 402140795 id 881 -epd br1krb1n/2qppppp/pp3n2/8/1P4P1/8/P1PPPP1P/1RQKRBNN w EBeb - 0 9 +epd br1krb1n/2qppppp/pp3n2/8/1P4P1/8/P1PPPP1P/1RQKRBNN w EBeb - perft 1 24 perft 2 945 perft 3 23943 @@ -7940,7 +7940,7 @@ perft 5 25019636 perft 6 959651619 id 882 -epd brqkr1nb/2ppp1pp/1p2np2/p7/2P1PN2/8/PP1P1PPP/BRQKRN1B w EBeb - 0 9 +epd brqkr1nb/2ppp1pp/1p2np2/p7/2P1PN2/8/PP1P1PPP/BRQKRN1B w EBeb - perft 1 28 perft 2 675 perft 3 19728 @@ -7949,7 +7949,7 @@ perft 5 15516491 perft 6 417396563 id 883 -epd rbbqkrnn/3pppp1/p7/1pp4p/2P1P2P/8/PP1P1PP1/RBBQKRNN w FAfa - 0 9 +epd rbbqkrnn/3pppp1/p7/1pp4p/2P1P2P/8/PP1P1PP1/RBBQKRNN w FAfa - perft 1 26 perft 2 671 perft 3 18164 @@ -7958,7 +7958,7 @@ perft 5 14072641 perft 6 404960259 id 884 -epd rqbbkr1n/pp1p1p1p/4pn2/2p3p1/4P1P1/3P3P/PPP2P2/RQBBKRNN w FAfa - 0 9 +epd rqbbkr1n/pp1p1p1p/4pn2/2p3p1/4P1P1/3P3P/PPP2P2/RQBBKRNN w FAfa - perft 1 22 perft 2 633 perft 3 14629 @@ -7967,7 +7967,7 @@ perft 5 10776416 perft 6 335689685 id 885 -epd rqbkrbnn/p1ppp3/1p3pp1/7p/3P4/P1P5/1PQ1PPPP/R1BKRBNN w EAea - 0 9 +epd rqbkrbnn/p1ppp3/1p3pp1/7p/3P4/P1P5/1PQ1PPPP/R1BKRBNN w EAea - perft 1 32 perft 2 607 perft 3 20339 @@ -7976,7 +7976,7 @@ perft 5 15586203 perft 6 383515709 id 886 -epd rqbkrnn1/pp2ppbp/3p4/2p3p1/2P5/1P3N1P/P2PPPP1/RQBKRN1B w EAea - 1 9 +epd rqbkrnn1/pp2ppbp/3p4/2p3p1/2P5/1P3N1P/P2PPPP1/RQBKRN1B w EAea - perft 1 29 perft 2 943 perft 3 28732 @@ -7985,7 +7985,7 @@ perft 5 28761841 perft 6 907579129 id 887 -epd rbqkb1nn/1ppppr1p/p5p1/5p2/1P6/2P4P/P1KPPPP1/RBQ1BRNN w a - 1 9 +epd rbqkb1nn/1ppppr1p/p5p1/5p2/1P6/2P4P/P1KPPPP1/RBQ1BRNN w a - perft 1 22 perft 2 441 perft 3 10403 @@ -7994,7 +7994,7 @@ perft 5 5784206 perft 6 140934555 id 888 -epd rqkb1rnn/1pp1pp1p/p5p1/1b1p4/3P4/P5P1/RPP1PP1P/1QKBBRNN w Ffa - 1 9 +epd rqkb1rnn/1pp1pp1p/p5p1/1b1p4/3P4/P5P1/RPP1PP1P/1QKBBRNN w Ffa - perft 1 21 perft 2 505 perft 3 11592 @@ -8003,7 +8003,7 @@ perft 5 7147063 perft 6 188559137 id 889 -epd rq1rbbnn/pkp1ppp1/3p3p/1p2N1P1/8/8/PPPPPP1P/RQKRBB1N w DA - 0 9 +epd rq1rbbnn/pkp1ppp1/3p3p/1p2N1P1/8/8/PPPPPP1P/RQKRBB1N w DA - perft 1 27 perft 2 608 perft 3 16419 @@ -8012,7 +8012,7 @@ perft 5 10808908 perft 6 268393274 id 890 -epd rqkrb2b/p2ppppp/2p3nn/1p6/5P2/PP1P4/2P1P1PP/RQKRBNNB w DAda - 1 9 +epd rqkrb2b/p2ppppp/2p3nn/1p6/5P2/PP1P4/2P1P1PP/RQKRBNNB w DAda - perft 1 30 perft 2 749 perft 3 21563 @@ -8021,7 +8021,7 @@ perft 5 16916813 perft 6 485406712 id 891 -epd rbqkr1bn/pp1ppp2/2p1n2p/6p1/8/4BPNP/PPPPP1P1/RBQKRN2 w EAea - 0 9 +epd rbqkr1bn/pp1ppp2/2p1n2p/6p1/8/4BPNP/PPPPP1P1/RBQKRN2 w EAea - perft 1 23 perft 2 600 perft 3 15082 @@ -8030,7 +8030,7 @@ perft 5 11041820 perft 6 314327867 id 892 -epd rqkbrnb1/2ppp1pp/pp3pn1/8/5P2/B2P4/PPP1P1PP/RQKBRN1N w EAea - 2 9 +epd rqkbrnb1/2ppp1pp/pp3pn1/8/5P2/B2P4/PPP1P1PP/RQKBRN1N w EAea - perft 1 22 perft 2 569 perft 3 13541 @@ -8039,7 +8039,7 @@ perft 5 9395816 perft 6 269460607 id 893 -epd rqkrnbb1/p1p1pppp/1p4n1/3p4/7P/P3P3/1PPPBPP1/RQKRN1BN w DAda - 0 9 +epd rqkrnbb1/p1p1pppp/1p4n1/3p4/7P/P3P3/1PPPBPP1/RQKRN1BN w DAda - perft 1 27 perft 2 579 perft 3 15565 @@ -8048,7 +8048,7 @@ perft 5 10238486 perft 6 266047417 id 894 -epd rqkrn1bb/p1ppp1pp/4n3/1p6/6p1/4N3/PPPPPPPP/RQKR2BB w DAda - 0 9 +epd rqkrn1bb/p1ppp1pp/4n3/1p6/6p1/4N3/PPPPPPPP/RQKR2BB w DAda - perft 1 20 perft 2 462 perft 3 10234 @@ -8057,7 +8057,7 @@ perft 5 6563859 perft 6 193376359 id 895 -epd bbrkqr2/pppp1ppp/6nn/8/2P1p3/3PP2N/PP3PPP/BBRKQR1N w FCfc - 0 9 +epd bbrkqr2/pppp1ppp/6nn/8/2P1p3/3PP2N/PP3PPP/BBRKQR1N w FCfc - perft 1 28 perft 2 724 perft 3 21688 @@ -8066,7 +8066,7 @@ perft 5 19318355 perft 6 593204629 id 896 -epd brk1qrnn/1pppbppp/4p3/8/1p6/P1P4P/3PPPP1/BRKBQRNN w FBfb - 1 9 +epd brk1qrnn/1pppbppp/4p3/8/1p6/P1P4P/3PPPP1/BRKBQRNN w FBfb - perft 1 24 perft 2 662 perft 3 16920 @@ -8075,7 +8075,7 @@ perft 5 12610387 perft 6 355969349 id 897 -epd 1r1qrbnn/p1pkpppp/1p1p4/8/3P1PP1/P4b2/1PP1P2P/BRKQRBNN w EB - 1 9 +epd 1r1qrbnn/p1pkpppp/1p1p4/8/3P1PP1/P4b2/1PP1P2P/BRKQRBNN w EB - perft 1 22 perft 2 696 perft 3 17021 @@ -8084,7 +8084,7 @@ perft 5 13697382 perft 6 401903030 id 898 -epd 1rkqrnnb/p1p1p1pp/1p1p4/3b1p1N/4P3/5N2/PPPP1PPP/BRKQR2B w EBeb - 1 9 +epd 1rkqrnnb/p1p1p1pp/1p1p4/3b1p1N/4P3/5N2/PPPP1PPP/BRKQR2B w EBeb - perft 1 29 perft 2 887 perft 3 27035 @@ -8093,7 +8093,7 @@ perft 5 26051242 perft 6 791718847 id 899 -epd rbbkq1rn/pppppppp/7n/8/P7/3P3P/1PPKPPP1/RBB1QRNN w a - 3 9 +epd rbbkq1rn/pppppppp/7n/8/P7/3P3P/1PPKPPP1/RBB1QRNN w a - perft 1 22 perft 2 417 perft 3 9900 @@ -8102,7 +8102,7 @@ perft 5 5505063 perft 6 134818483 id 900 -epd rkbbqr1n/1p1pppp1/2p2n2/p4NBp/8/3P4/PPP1PPPP/RK1BQRN1 w FAfa - 0 9 +epd rkbbqr1n/1p1pppp1/2p2n2/p4NBp/8/3P4/PPP1PPPP/RK1BQRN1 w FAfa - perft 1 37 perft 2 832 perft 3 30533 @@ -8111,7 +8111,7 @@ perft 5 26676373 perft 6 673756141 id 901 -epd rkbqrb1n/3pBppp/ppp2n2/8/8/P2P4/1PP1PPPP/RK1QRBNN w EAea - 0 9 +epd rkbqrb1n/3pBppp/ppp2n2/8/8/P2P4/1PP1PPPP/RK1QRBNN w EAea - perft 1 28 perft 2 685 perft 3 19718 @@ -8120,7 +8120,7 @@ perft 5 16033316 perft 6 482288814 id 902 -epd rkb1rn1b/ppppqppp/4p3/8/1P2n1P1/5Q2/P1PP1P1P/RKB1RNNB w EAea - 2 9 +epd rkb1rn1b/ppppqppp/4p3/8/1P2n1P1/5Q2/P1PP1P1P/RKB1RNNB w EAea - perft 1 37 perft 2 1158 perft 3 40114 @@ -8129,7 +8129,7 @@ perft 5 44672979 perft 6 1389312729 id 903 -epd r1kqbrnn/pp1pp1p1/7p/2P2p2/5b2/3P4/P1P1P1PP/RBKQBRNN w FAfa - 0 9 +epd r1kqbrnn/pp1pp1p1/7p/2P2p2/5b2/3P4/P1P1P1PP/RBKQBRNN w FAfa - perft 1 5 perft 2 161 perft 3 4745 @@ -8138,7 +8138,7 @@ perft 5 4734999 perft 6 157499039 id 904 -epd rkqbbr1n/ppp1ppp1/8/Q2p3p/4n3/3P1P2/PPP1P1PP/RK1BBRNN w FAfa - 2 9 +epd rkqbbr1n/ppp1ppp1/8/Q2p3p/4n3/3P1P2/PPP1P1PP/RK1BBRNN w FAfa - perft 1 38 perft 2 1144 perft 3 40433 @@ -8147,7 +8147,7 @@ perft 5 43832975 perft 6 1366087771 id 905 -epd rkqrbbn1/p1ppppp1/Bp5p/8/P6n/2P1P3/1P1P1PPP/RKQRB1NN w DAda - 0 9 +epd rkqrbbn1/p1ppppp1/Bp5p/8/P6n/2P1P3/1P1P1PPP/RKQRB1NN w DAda - perft 1 28 perft 2 551 perft 3 15488 @@ -8156,7 +8156,7 @@ perft 5 9944107 perft 6 251179183 id 906 -epd rkqrb1nb/1ppp1ppp/p7/4p3/5n2/3P2N1/PPPQPPPP/RK1RB1NB w DAda - 0 9 +epd rkqrb1nb/1ppp1ppp/p7/4p3/5n2/3P2N1/PPPQPPPP/RK1RB1NB w DAda - perft 1 26 perft 2 690 perft 3 19877 @@ -8165,7 +8165,7 @@ perft 5 15965907 perft 6 418191735 id 907 -epd rbkqrnbn/pppp1p2/4p1p1/7p/7P/P2P4/BPP1PPP1/R1KQRNBN w EAea - 0 9 +epd rbkqrnbn/pppp1p2/4p1p1/7p/7P/P2P4/BPP1PPP1/R1KQRNBN w EAea - perft 1 27 perft 2 515 perft 3 13992 @@ -8174,7 +8174,7 @@ perft 5 8792550 perft 6 218658292 id 908 -epd rkqbrnbn/pp1ppp2/8/2p3p1/P1P4p/5P2/1PKPP1PP/R1QBRNBN w ea - 0 9 +epd rkqbrnbn/pp1ppp2/8/2p3p1/P1P4p/5P2/1PKPP1PP/R1QBRNBN w ea - perft 1 27 perft 2 627 perft 3 16843 @@ -8183,7 +8183,7 @@ perft 5 11978698 perft 6 328434174 id 909 -epd rkqrnbbn/1p2pp1p/3p2p1/p1p5/P5PP/3N4/1PPPPP2/RKQR1BBN w DAda - 0 9 +epd rkqrnbbn/1p2pp1p/3p2p1/p1p5/P5PP/3N4/1PPPPP2/RKQR1BBN w DAda - perft 1 23 perft 2 624 perft 3 15512 @@ -8192,7 +8192,7 @@ perft 5 11960861 perft 6 367311176 id 910 -epd rk2rnbb/ppqppppp/2pn4/8/1P3P2/6P1/P1PPP1NP/RKQR1NBB w DAa - 1 9 +epd rk2rnbb/ppqppppp/2pn4/8/1P3P2/6P1/P1PPP1NP/RKQR1NBB w DAa - perft 1 27 perft 2 727 perft 3 20206 @@ -8201,7 +8201,7 @@ perft 5 16633696 perft 6 505212747 id 911 -epd b1krrqnn/pp1ppp1p/2p3p1/8/P3Pb1P/1P6/2PP1PP1/BBRKRQNN w EC - 0 9 +epd b1krrqnn/pp1ppp1p/2p3p1/8/P3Pb1P/1P6/2PP1PP1/BBRKRQNN w EC - perft 1 32 perft 2 943 perft 3 30759 @@ -8210,7 +8210,7 @@ perft 5 28672582 perft 6 800922511 id 912 -epd 1rkbrqnn/p1pp1ppp/1p6/8/P2Pp3/8/1PPKPPQP/BR1BR1NN w eb - 0 9 +epd 1rkbrqnn/p1pp1ppp/1p6/8/P2Pp3/8/1PPKPPQP/BR1BR1NN w eb - perft 1 28 perft 2 916 perft 3 24892 @@ -8219,7 +8219,7 @@ perft 5 22840279 perft 6 759318058 id 913 -epd brkrqb1n/1pppp1pp/p7/3n1p2/P5P1/3PP3/1PP2P1P/BRKRQBNN w DBdb - 0 9 +epd brkrqb1n/1pppp1pp/p7/3n1p2/P5P1/3PP3/1PP2P1P/BRKRQBNN w DBdb - perft 1 27 perft 2 669 perft 3 18682 @@ -8228,7 +8228,7 @@ perft 5 13956472 perft 6 380267099 id 914 -epd brkrqnnb/3pppp1/1p6/p1p4p/2P3P1/6N1/PP1PPP1P/BRKRQ1NB w DBdb - 0 9 +epd brkrqnnb/3pppp1/1p6/p1p4p/2P3P1/6N1/PP1PPP1P/BRKRQ1NB w DBdb - perft 1 29 perft 2 699 perft 3 20042 @@ -8237,7 +8237,7 @@ perft 5 15093909 perft 6 406594531 id 915 -epd r1bkrq1n/pp2pppp/3b1n2/2pp2B1/6P1/3P1P2/PPP1P2P/RB1KRQNN w EAea - 2 9 +epd r1bkrq1n/pp2pppp/3b1n2/2pp2B1/6P1/3P1P2/PPP1P2P/RB1KRQNN w EAea - perft 1 27 perft 2 835 perft 3 22848 @@ -8246,7 +8246,7 @@ perft 5 19867800 perft 6 631209313 id 916 -epd rk1brq1n/p1p1pppp/3p1n2/1p3b2/4P3/2NQ4/PPPP1PPP/RKBBR2N w EAea - 4 9 +epd rk1brq1n/p1p1pppp/3p1n2/1p3b2/4P3/2NQ4/PPPP1PPP/RKBBR2N w EAea - perft 1 36 perft 2 1004 perft 3 35774 @@ -8255,7 +8255,7 @@ perft 5 35143142 perft 6 966310885 id 917 -epd rkbrqbnn/1p2ppp1/B1p5/p2p3p/4P2P/8/PPPP1PP1/RKBRQ1NN w DAda - 0 9 +epd rkbrqbnn/1p2ppp1/B1p5/p2p3p/4P2P/8/PPPP1PP1/RKBRQ1NN w DAda - perft 1 27 perft 2 748 perft 3 21005 @@ -8264,7 +8264,7 @@ perft 5 17597073 perft 6 515304215 id 918 -epd rkbrqn1b/pp1pp1pp/2p2p2/5n2/8/2P2P2/PP1PP1PP/RKBRQ1NB w DAda - 0 9 +epd rkbrqn1b/pp1pp1pp/2p2p2/5n2/8/2P2P2/PP1PP1PP/RKBRQ1NB w DAda - perft 1 20 perft 2 479 perft 3 10485 @@ -8273,7 +8273,7 @@ perft 5 6253775 perft 6 167767913 id 919 -epd rbkrbnn1/ppppp1pp/5q2/5p2/5P2/P3P2N/1PPP2PP/RBKRBQ1N w DAda - 3 9 +epd rbkrbnn1/ppppp1pp/5q2/5p2/5P2/P3P2N/1PPP2PP/RBKRBQ1N w DAda - perft 1 28 perft 2 947 perft 3 26900 @@ -8282,7 +8282,7 @@ perft 5 26007841 perft 6 838704143 id 920 -epd rkr1bqnn/1ppp1p1p/p5p1/4p3/3PP2b/2P2P2/PP4PP/RKRBBQNN w CAca - 0 9 +epd rkr1bqnn/1ppp1p1p/p5p1/4p3/3PP2b/2P2P2/PP4PP/RKRBBQNN w CAca - perft 1 31 perft 2 1004 perft 3 32006 @@ -8291,7 +8291,7 @@ perft 5 32688124 perft 6 1024529879 id 921 -epd rkrqbbnn/pppp3p/8/4ppp1/1PP4P/8/P2PPPP1/RKRQBBNN w CAca - 0 9 +epd rkrqbbnn/pppp3p/8/4ppp1/1PP4P/8/P2PPPP1/RKRQBBNN w CAca - perft 1 24 perft 2 717 perft 3 18834 @@ -8300,7 +8300,7 @@ perft 5 15844525 perft 6 484884485 id 922 -epd rkrqbn1b/pppp2pp/8/4pp2/1P1P2n1/5N2/P1P1PP1P/RKRQBN1B w CAca - 0 9 +epd rkrqbn1b/pppp2pp/8/4pp2/1P1P2n1/5N2/P1P1PP1P/RKRQBN1B w CAca - perft 1 25 perft 2 718 perft 3 19654 @@ -8309,7 +8309,7 @@ perft 5 17257753 perft 6 537354146 id 923 -epd rbkrqnbn/p1p1ppp1/1p1p4/8/3PP2p/2PB4/PP3PPP/R1KRQNBN w DAda - 0 9 +epd rbkrqnbn/p1p1ppp1/1p1p4/8/3PP2p/2PB4/PP3PPP/R1KRQNBN w DAda - perft 1 30 perft 2 754 perft 3 23298 @@ -8318,7 +8318,7 @@ perft 5 19338246 perft 6 532603566 id 924 -epd 1krbqnbn/1p2pppp/r1pp4/p7/8/1P1P2PP/P1P1PP2/RKRBQNBN w CAc - 0 9 +epd 1krbqnbn/1p2pppp/r1pp4/p7/8/1P1P2PP/P1P1PP2/RKRBQNBN w CAc - perft 1 21 perft 2 566 perft 3 13519 @@ -8327,7 +8327,7 @@ perft 5 9700847 perft 6 279864836 id 925 -epd rkrq1b2/pppppppb/3n2np/2N5/4P3/7P/PPPP1PP1/RKRQ1BBN w CAca - 1 9 +epd rkrq1b2/pppppppb/3n2np/2N5/4P3/7P/PPPP1PP1/RKRQ1BBN w CAca - perft 1 33 perft 2 654 perft 3 21708 @@ -8336,7 +8336,7 @@ perft 5 15990307 perft 6 382218272 id 926 -epd rkr1nnbb/ppp2p1p/3p1qp1/4p3/P5P1/3PN3/1PP1PP1P/RKRQN1BB w CAca - 1 9 +epd rkr1nnbb/ppp2p1p/3p1qp1/4p3/P5P1/3PN3/1PP1PP1P/RKRQN1BB w CAca - perft 1 28 perft 2 715 perft 3 20361 @@ -8345,7 +8345,7 @@ perft 5 16303092 perft 6 468666425 id 927 -epd bbrkrnqn/1p1ppppp/8/8/p2pP3/PP6/2P2PPP/BBRKRNQN w ECec - 0 9 +epd bbrkrnqn/1p1ppppp/8/8/p2pP3/PP6/2P2PPP/BBRKRNQN w ECec - perft 1 24 perft 2 757 perft 3 19067 @@ -8354,7 +8354,7 @@ perft 5 15957628 perft 6 509307623 id 928 -epd brkbrnqn/ppp2p2/4p3/P2p2pp/6P1/5P2/1PPPP2P/BRKBRNQN w EBeb - 0 9 +epd brkbrnqn/ppp2p2/4p3/P2p2pp/6P1/5P2/1PPPP2P/BRKBRNQN w EBeb - perft 1 25 perft 2 548 perft 3 14563 @@ -8363,7 +8363,7 @@ perft 5 9688526 perft 6 247750144 id 929 -epd brkr1bqn/1pppppp1/3n3p/1p6/P7/4P1P1/1PPP1P1P/BRKRN1QN w DBdb - 0 9 +epd brkr1bqn/1pppppp1/3n3p/1p6/P7/4P1P1/1PPP1P1P/BRKRN1QN w DBdb - perft 1 19 perft 2 359 perft 3 7430 @@ -8372,7 +8372,7 @@ perft 5 3521652 perft 6 81787718 id 930 -epd brkr1qnb/pppp2pp/2B1p3/5p2/2n5/6PP/PPPPPPN1/BRKR1QN1 w DBdb - 1 9 +epd brkr1qnb/pppp2pp/2B1p3/5p2/2n5/6PP/PPPPPPN1/BRKR1QN1 w DBdb - perft 1 27 perft 2 854 perft 3 23303 @@ -8381,7 +8381,7 @@ perft 5 20558538 perft 6 667089231 id 931 -epd rbbkrnqn/p1p1p1pp/8/1p1p4/1P1Pp3/6N1/P1P2PPP/RBBKRNQ1 w EAea - 0 9 +epd rbbkrnqn/p1p1p1pp/8/1p1p4/1P1Pp3/6N1/P1P2PPP/RBBKRNQ1 w EAea - perft 1 28 perft 2 723 perft 3 19844 @@ -8390,7 +8390,7 @@ perft 5 14621108 perft 6 397454100 id 932 -epd rkbbrn1n/pppppp2/5q1p/6p1/3P3P/4P3/PPP2PP1/RKBBRNQN w EAea - 1 9 +epd rkbbrn1n/pppppp2/5q1p/6p1/3P3P/4P3/PPP2PP1/RKBBRNQN w EAea - perft 1 25 perft 2 741 perft 3 19224 @@ -8399,7 +8399,7 @@ perft 5 15605840 perft 6 485037906 id 933 -epd rkbr1bq1/ppnppppp/6n1/2p5/2P1N2P/8/PP1PPPP1/RKBRNBQ1 w DAda - 3 9 +epd rkbr1bq1/ppnppppp/6n1/2p5/2P1N2P/8/PP1PPPP1/RKBRNBQ1 w DAda - perft 1 24 perft 2 547 perft 3 14359 @@ -8408,7 +8408,7 @@ perft 5 9410221 perft 6 234041078 id 934 -epd 1kbrnqnb/r1ppppp1/8/pp5p/8/1P1NP3/P1PP1PPP/RKB1RQNB w Ad - 2 9 +epd 1kbrnqnb/r1ppppp1/8/pp5p/8/1P1NP3/P1PP1PPP/RKB1RQNB w Ad - perft 1 26 perft 2 618 perft 3 17305 @@ -8417,7 +8417,7 @@ perft 5 13112297 perft 6 357030697 id 935 -epd rbkrb1qn/1pp1ppp1/3pn2p/pP6/8/4N1P1/P1PPPP1P/RBKRB1QN w DAda - 0 9 +epd rbkrb1qn/1pp1ppp1/3pn2p/pP6/8/4N1P1/P1PPPP1P/RBKRB1QN w DAda - perft 1 21 perft 2 544 perft 3 12492 @@ -8426,7 +8426,7 @@ perft 5 8381483 perft 6 236013157 id 936 -epd rkrbbnqn/ppppp3/5p2/6pp/5PBP/4P3/PPPP2P1/RKR1BNQN w CAca - 0 9 +epd rkrbbnqn/ppppp3/5p2/6pp/5PBP/4P3/PPPP2P1/RKR1BNQN w CAca - perft 1 30 perft 2 891 perft 3 25435 @@ -8435,7 +8435,7 @@ perft 5 21894752 perft 6 669256602 id 937 -epd rkr1bb1n/ppppp1pp/5p2/4n3/3QP3/5P2/RPPP2PP/1KRNBB1N w Cca - 1 9 +epd rkr1bb1n/ppppp1pp/5p2/4n3/3QP3/5P2/RPPP2PP/1KRNBB1N w Cca - perft 1 45 perft 2 1172 perft 3 51766 @@ -8444,7 +8444,7 @@ perft 5 57856784 perft 6 1501852662 id 938 -epd rkr1bqnb/pp1ppppp/8/2pN4/1P6/5N2/P1PPnPPP/RKR1BQ1B w CAca - 0 9 +epd rkr1bqnb/pp1ppppp/8/2pN4/1P6/5N2/P1PPnPPP/RKR1BQ1B w CAca - perft 1 28 perft 2 730 perft 3 20511 @@ -8453,7 +8453,7 @@ perft 5 16323242 perft 6 463032124 id 939 -epd rbkrnqb1/2ppppp1/p5np/1p6/8/3N4/PPPPPPPP/RBKRQNB1 w DAda - 2 9 +epd rbkrnqb1/2ppppp1/p5np/1p6/8/3N4/PPPPPPPP/RBKRQNB1 w DAda - perft 1 20 perft 2 417 perft 3 9159 @@ -8462,7 +8462,7 @@ perft 5 5180716 perft 6 133936564 id 940 -epd rkrbnqb1/p1pppnpp/5p2/1p6/2P5/1P1P1N2/P3PPPP/RKRB1QBN w CAca - 0 9 +epd rkrbnqb1/p1pppnpp/5p2/1p6/2P5/1P1P1N2/P3PPPP/RKRB1QBN w CAca - perft 1 25 perft 2 546 perft 3 14039 @@ -8471,7 +8471,7 @@ perft 5 8813781 perft 6 222026485 id 941 -epd rkr1qbbn/ppppppp1/4n3/7p/8/P7/KPPPPPPP/R1RNQBBN w ca - 0 9 +epd rkr1qbbn/ppppppp1/4n3/7p/8/P7/KPPPPPPP/R1RNQBBN w ca - perft 1 22 perft 2 484 perft 3 11458 @@ -8480,7 +8480,7 @@ perft 5 6633319 perft 6 163291279 id 942 -epd rkrnqnb1/1ppppp2/p5p1/7p/8/P1bPP3/1PP1QPPP/RKRN1NBB w CAca - 0 9 +epd rkrnqnb1/1ppppp2/p5p1/7p/8/P1bPP3/1PP1QPPP/RKRN1NBB w CAca - perft 1 22 perft 2 636 perft 3 15526 @@ -8489,7 +8489,7 @@ perft 5 11614241 perft 6 331083405 id 943 -epd b2krn1q/p1rppppp/1Q3n2/2p1b3/1P4P1/8/P1PPPP1P/BBRKRNN1 w ECe - 3 9 +epd b2krn1q/p1rppppp/1Q3n2/2p1b3/1P4P1/8/P1PPPP1P/BBRKRNN1 w ECe - perft 1 36 perft 2 1192 perft 3 42945 @@ -8498,7 +8498,7 @@ perft 5 50382104 perft 6 1650202838 id 944 -epd brkbrnn1/pp1pppp1/7q/2p5/6Pp/4P1NP/PPPP1P2/BRKBR1NQ w EBeb - 2 9 +epd brkbrnn1/pp1pppp1/7q/2p5/6Pp/4P1NP/PPPP1P2/BRKBR1NQ w EBeb - perft 1 30 perft 2 978 perft 3 29593 @@ -8507,7 +8507,7 @@ perft 5 29205057 perft 6 936568065 id 945 -epd brkrnb1q/pp1p1ppp/2p1p3/5n2/1P6/5N1N/P1PPPPPP/BRKR1B1Q w DBdb - 1 9 +epd brkrnb1q/pp1p1ppp/2p1p3/5n2/1P6/5N1N/P1PPPPPP/BRKR1B1Q w DBdb - perft 1 31 perft 2 897 perft 3 27830 @@ -8516,7 +8516,7 @@ perft 5 25423729 perft 6 755334868 id 946 -epd brkr1nqb/pp1p1pp1/2pn3p/P3p3/4P3/6P1/1PPP1P1P/BRKRNNQB w DBdb - 0 9 +epd brkr1nqb/pp1p1pp1/2pn3p/P3p3/4P3/6P1/1PPP1P1P/BRKRNNQB w DBdb - perft 1 19 perft 2 382 perft 3 8052 @@ -8525,7 +8525,7 @@ perft 5 4232274 perft 6 103537333 id 947 -epd r1bkrn1q/ppbppppp/5n2/2p5/3P4/P6N/1PP1PPPP/RBBKRNQ1 w EAea - 3 9 +epd r1bkrn1q/ppbppppp/5n2/2p5/3P4/P6N/1PP1PPPP/RBBKRNQ1 w EAea - perft 1 27 perft 2 822 perft 3 22551 @@ -8534,7 +8534,7 @@ perft 5 19115128 perft 6 578210135 id 948 -epd rkbbrnnq/pp2pppp/8/2pp4/P1P5/1P3P2/3PP1PP/RKBBRNNQ w EAea - 1 9 +epd rkbbrnnq/pp2pppp/8/2pp4/P1P5/1P3P2/3PP1PP/RKBBRNNQ w EAea - perft 1 23 perft 2 643 perft 3 15410 @@ -8543,7 +8543,7 @@ perft 5 11170489 perft 6 329615708 id 949 -epd rkbr1b1q/p1pppppp/1p1n4/7n/5QP1/3N4/PPPPPP1P/RKBR1BN1 w DAda - 4 9 +epd rkbr1b1q/p1pppppp/1p1n4/7n/5QP1/3N4/PPPPPP1P/RKBR1BN1 w DAda - perft 1 37 perft 2 943 perft 3 34382 @@ -8552,7 +8552,7 @@ perft 5 31568111 perft 6 842265141 id 950 -epd rkbr1nqb/pppp2np/8/4ppp1/1P6/6N1/P1PPPPPP/RKBRN1QB w DAda - 1 9 +epd rkbr1nqb/pppp2np/8/4ppp1/1P6/6N1/P1PPPPPP/RKBRN1QB w DAda - perft 1 23 perft 2 574 perft 3 13260 @@ -8561,7 +8561,7 @@ perft 5 9020291 perft 6 261247606 id 951 -epd rbkr1nnq/p1p1pp1p/1p4p1/3p4/b3P3/4N3/PPPPNPPP/RBKRB1Q1 w DAda - 0 9 +epd rbkr1nnq/p1p1pp1p/1p4p1/3p4/b3P3/4N3/PPPPNPPP/RBKRB1Q1 w DAda - perft 1 26 perft 2 900 perft 3 23414 @@ -8570,7 +8570,7 @@ perft 5 21653203 perft 6 745802405 id 952 -epd rkrbb1nq/p2pppp1/1p4n1/2p4p/3N4/4P1P1/PPPP1P1P/RKRBBN1Q w CAca - 0 9 +epd rkrbb1nq/p2pppp1/1p4n1/2p4p/3N4/4P1P1/PPPP1P1P/RKRBBN1Q w CAca - perft 1 32 perft 2 697 perft 3 22231 @@ -8579,7 +8579,7 @@ perft 5 17150175 perft 6 441578567 id 953 -epd rkrnbb1q/pp2pp1p/6pn/2pp4/2B1P2P/8/PPPP1PP1/RKRNB1NQ w CAca - 0 9 +epd rkrnbb1q/pp2pp1p/6pn/2pp4/2B1P2P/8/PPPP1PP1/RKRNB1NQ w CAca - perft 1 28 perft 2 854 perft 3 23853 @@ -8588,7 +8588,7 @@ perft 5 21823412 perft 6 712787248 id 954 -epd rk2bnqb/pprpppp1/4n2p/2p5/P7/3P2NP/1PP1PPP1/RKRNB1QB w CAa - 1 9 +epd rk2bnqb/pprpppp1/4n2p/2p5/P7/3P2NP/1PP1PPP1/RKRNB1QB w CAa - perft 1 26 perft 2 596 perft 3 16251 @@ -8597,7 +8597,7 @@ perft 5 11758184 perft 6 323043654 id 955 -epd r1krnnbq/pp1ppp1p/6p1/2p5/2P5/P3P3/Rb1P1PPP/1BKRNNBQ w Dda - 0 9 +epd r1krnnbq/pp1ppp1p/6p1/2p5/2P5/P3P3/Rb1P1PPP/1BKRNNBQ w Dda - perft 1 2 perft 2 61 perft 3 1312 @@ -8606,7 +8606,7 @@ perft 5 937188 perft 6 28753562 id 956 -epd 1krbnnbq/1pp1p1pp/r7/p2p1p2/3PP3/2P3P1/PP3P1P/RKRBNNBQ w CAc - 0 9 +epd 1krbnnbq/1pp1p1pp/r7/p2p1p2/3PP3/2P3P1/PP3P1P/RKRBNNBQ w CAc - perft 1 30 perft 2 953 perft 3 28033 @@ -8615,7 +8615,7 @@ perft 5 25531358 perft 6 787205262 id 957 -epd rkr1nbbq/2ppp1pp/1pn5/p4p2/P6P/3P4/1PP1PPPB/RKRNNB1Q w CAca - 1 9 +epd rkr1nbbq/2ppp1pp/1pn5/p4p2/P6P/3P4/1PP1PPPB/RKRNNB1Q w CAca - perft 1 24 perft 2 645 perft 3 15689 @@ -8624,7 +8624,7 @@ perft 5 11484012 perft 6 341262639 id 958 -epd rkrnnqbb/p1ppp2p/Qp6/4Pp2/5p2/8/PPPP2PP/RKRNN1BB w CAca - 0 9 +epd rkrnnqbb/p1ppp2p/Qp6/4Pp2/5p2/8/PPPP2PP/RKRNN1BB w CAca - perft 1 35 perft 2 929 perft 3 32020 @@ -8633,7 +8633,7 @@ perft 5 31272517 perft 6 915268405 id 959 -epd bbq1nr1r/pppppk1p/2n2p2/6p1/P4P2/4P1P1/1PPP3P/BBQNNRKR w HF - 1 9 +epd bbq1nr1r/pppppk1p/2n2p2/6p1/P4P2/4P1P1/1PPP3P/BBQNNRKR w HF - perft 1 23 perft 2 589 perft 3 14744 From eaa6eb3b023e92441f9de810785bb1716dd4d0b5 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 4 May 2024 18:03:34 +0200 Subject: [PATCH 029/116] Fixup invalid EPD in crazyhouse.perft file --- examples/perft/crazyhouse.perft | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/perft/crazyhouse.perft b/examples/perft/crazyhouse.perft index 42ccd68a6..ef07eac04 100644 --- a/examples/perft/crazyhouse.perft +++ b/examples/perft/crazyhouse.perft @@ -18,7 +18,7 @@ perft 3 58057 perft 4 2083382 id zh-promoted -epd 4k3/1Q~6/8/8/4b3/8/Kpp5/8/ b - - 0 1 +epd 4k3/1Q~6/8/8/4b3/8/Kpp5/8/ b - - perft 1 20 perft 2 360 perft 3 5445 From 122963b82e408417e2b4fd17643a61f42f4e9ae9 Mon Sep 17 00:00:00 2001 From: 3d12 <3d12@users.noreply.github.com> Date: Thu, 16 May 2024 20:09:41 -0500 Subject: [PATCH 030/116] Add rookognition --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 7a677a451..c80957a37 100644 --- a/README.rst +++ b/README.rst @@ -351,6 +351,7 @@ If you like, share interesting things you are using python-chess for, for exampl * a `browser based PGN viewer `_ written in PyScript – https://github.com/nmstoker/ChessMatchViewer * an accessible chessboard that allows blind and visually impaired players to play chess against Stockfish – https://github.com/blindpandas/chessmart * analyse games to extract various statistics – https://github.com/mvrozanti/chess-playground +* a web-based chess vision exercise – https://github.com/3d12/rookognition Prior art From 102542a3c07c74557038ced2086c0d9bbb813cf4 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Sun, 26 May 2024 03:46:30 -0700 Subject: [PATCH 031/116] Remove GameNodeComment class Changes PR to a simpler change that is not backwards compatible. --- chess/pgn.py | 196 +++++++++++---------------------------------------- test.py | 66 +++++++---------- 2 files changed, 65 insertions(+), 197 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index e462a0750..b40ce257c 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -130,6 +130,10 @@ def repl(match: typing.Match[str]) -> str: return repl +def _standardize_comments(comment: Union[str, list[str]]) -> list[str]: + return [] if not comment else [comment] if isinstance(comment, str) else comment + + TAG_ROSTER = ["Event", "Site", "Date", "Round", "White", "Black", "Result"] @@ -184,92 +188,6 @@ def __init__(self, node: ChildNode, *, is_variation: bool = False, sidelines: bo self.in_variation = False -class GameNodeComment: - """ - PGN Comment Storage - - A class that can hold one or more comments for a GameNode. - """ - - def __init__(self, comment: Union[str, list[str], GameNodeComment] = ""): - """Create a new comment.""" - self.set(comment._comments if isinstance(comment,GameNodeComment) else comment) - - def set(self, new_comment: Union[str, list[str]]) -> None: - """Replace the comment with a new comment or a list of comments.""" - self._comments = new_comment if isinstance(new_comment, list) else [new_comment] if new_comment else [] - - def pgn_format(self) -> str: - """Create a string representation of the comments in PGN format.""" - comments = map(lambda s: s.replace("{", ""), self._comments) - comments = map(lambda s: s.replace("}", "").strip(), comments) - return " ".join(f"{{ {comment} }}" for comment in comments if comment) - - def remove_empty(self) -> None: - """Remove empty comments from the comment list.""" - self._comments = list(filter(None, self._comments)) - - def append(self, new_comment: Union[str, list[str]]) -> None: - """Append one or more new comments to the end of the comment list.""" - if new_comment: - if isinstance(new_comment, str): - self._comments.append(new_comment) - else: - self._comments.extend(new_comment) - - def insert(self, index: int, new_comment: str) -> None: - """Insert a new comment before the specified index.""" - self._comments.insert(index, new_comment) - - def pop(self, index: int = -1) -> None: - """Delete comment at the given index (default is the last comment).""" - self._comments.pop(index) - - def __len__(self) -> int: - """Get the number of comments in this node.""" - return len(self._comments) - - def __getitem__(self, index: int) -> str: - """Get the comment at the given index.""" - return self._comments[index] - - def __setitem__(self, index: int, new_comment: str) -> None: - """Change the comment at the given index.""" - self._comments[index] = new_comment - - def __iter__(self) -> Iterator[str]: - """Return an iterator over all comments.""" - return iter(self._comments) - - def __add__(self, other: Union[str, list[str], GameNodeComment]) -> GameNodeComment: - """Create a new comment set by adding two comment sets with +.""" - if isinstance(other, GameNodeComment): - return GameNodeComment(self._comments + other._comments) - else: - new_node_comment = GameNodeComment(self._comments.copy()) - new_node_comment.append(other) - return new_node_comment - - def __eq__(self, other: object) -> bool: - """Check for equality between two comment sets.""" - if isinstance(other, str): - return (len(self) == 1 and self[0] == other) or (not self and not other) - elif isinstance(other, list): - return self._comments == other - elif isinstance(other, GameNodeComment): - return self._comments == other._comments - else: - return False - - def __repr__(self) -> str: - """Return a code-like representation of the class.""" - return f"{self.__class__.__name__}({self._comments})" - - def __str__(self) -> str: - """Return a string representation of the comments in PGN format.""" - return self.pgn_format() - - class GameNode(abc.ABC): parent: Optional[GameNode] """The parent node or ``None`` if this is the root node of the game.""" @@ -283,45 +201,13 @@ class GameNode(abc.ABC): variations: List[ChildNode] """A list of child nodes.""" - _comment: GameNodeComment + comments: list[str] """ A comment that goes behind the move leading to this node. Comments that occur before any moves are assigned to the root node. """ - @property - def comment(self) -> str: - return " ".join(self._comment) - - @comment.setter - def comment(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: - self._comment = GameNodeComment(new_comment) - - @property - def comments(self) -> GameNodeComment: - return self._comment - - @comments.setter - def comments(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: - self._comment = GameNodeComment(new_comment) - - _starting_comment: GameNodeComment - - @property - def starting_comment(self) -> str: - return " ".join(self._starting_comment) - - @starting_comment.setter - def starting_comment(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: - self._starting_comment = GameNodeComment(new_comment) - - @property - def starting_comments(self) -> GameNodeComment: - return self._starting_comment - - @starting_comments.setter - def starting_comments(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: - self._starting_comment = GameNodeComment(new_comment) + starting_comments: list[str] nags: Set[int] @@ -329,11 +215,11 @@ def __init__(self, *, comment: Union[str, list[str]] = "") -> None: self.parent = None self.move = None self.variations = [] - self.comments = GameNodeComment(comment) + self.comments = _standardize_comments(comment) # Deprecated: These should be properties of ChildNode, but need to # remain here for backwards compatibility. - self.starting_comments = GameNodeComment() + self.starting_comments = [] self.nags = set() @abc.abstractmethod @@ -549,7 +435,8 @@ def add_line(self, moves: Iterable[chess.Move], *, comment: Union[str, list[str] starting_comment = "" # Merge comment and NAGs. - node.comments.append(comment) + comments = _standardize_comments(comment) + node.comments.extend(comments) node.nags.update(nags) return node @@ -561,7 +448,7 @@ def eval(self) -> Optional[chess.engine.PovScore]: Complexity is `O(n)`. """ - match = EVAL_REGEX.search(self.comment) + match = EVAL_REGEX.search(" ".join(self.comments)) if not match: return None @@ -587,7 +474,7 @@ def eval_depth(self) -> Optional[int]: Complexity is `O(1)`. """ - match = EVAL_REGEX.search(self.comment) + match = EVAL_REGEX.search(" ".join(self.comments)) return int(match.group("depth")) if match and match.group("depth") else None def set_eval(self, score: Optional[chess.engine.PovScore], depth: Optional[int] = None) -> None: @@ -610,7 +497,7 @@ def set_eval(self, score: Optional[chess.engine.PovScore], depth: Optional[int] if found: break - self.comments.remove_empty() + self.comments = list(filter(None, self.comments)) if not found and eval: self.comments.append(eval) @@ -623,7 +510,7 @@ def arrows(self) -> List[chess.svg.Arrow]: Returns a list of :class:`arrows `. """ arrows = [] - for match in ARROWS_REGEX.finditer(self.comment): + for match in ARROWS_REGEX.finditer(" ".join(self.comments)): for group in match.group("arrows").split(","): arrows.append(chess.svg.Arrow.from_pgn(group)) @@ -648,7 +535,7 @@ def set_arrows(self, arrows: Iterable[Union[chess.svg.Arrow, Tuple[Square, Squar for index in range(len(self.comments)): self.comments[index] = ARROWS_REGEX.sub(_condense_affix(""), self.comments[index]) - self.comments.remove_empty() + self.comments = list(filter(None, self.comments)) prefix = "" if csl: @@ -667,7 +554,7 @@ def clock(self) -> Optional[float]: Returns the player's remaining time to the next time control after this move, in seconds. """ - match = CLOCK_REGEX.search(self.comment) + match = CLOCK_REGEX.search(" ".join(self.comments)) if match is None: return None return int(match.group("hours")) * 3600 + int(match.group("minutes")) * 60 + float(match.group("seconds")) @@ -692,7 +579,7 @@ def set_clock(self, seconds: Optional[float]) -> None: if found: break - self.comments.remove_empty() + self.comments = list(filter(None, self.comments)) if not found and clk: self.comments.append(clk) @@ -705,7 +592,7 @@ def emt(self) -> Optional[float]: Returns the player's elapsed move time use for the comment of this move, in seconds. """ - match = EMT_REGEX.search(self.comment) + match = EMT_REGEX.search(" ".join(self.comments)) if match is None: return None return int(match.group("hours")) * 3600 + int(match.group("minutes")) * 60 + float(match.group("seconds")) @@ -730,7 +617,7 @@ def set_emt(self, seconds: Optional[float]) -> None: if found: break - self.comments.remove_empty() + self.comments = list(filter(None, self.comments)) if not found and emt: self.comments.append(emt) @@ -789,7 +676,7 @@ class ChildNode(GameNode): move: chess.Move """The move leading to this node.""" - _starting_comment: GameNodeComment + starting_comments: list[str] """ A comment for the start of a variation. Only nodes that actually start a variation (:func:`~chess.pgn.GameNode.starts_variation()` @@ -797,14 +684,6 @@ class ChildNode(GameNode): a starting comment. """ - @property - def starting_comment(self) -> str: - return " ".join(self._starting_comment) - - @starting_comment.setter - def starting_comment(self, new_comment: Union[str, list[str], GameNodeComment]) -> None: - self._starting_comment = GameNodeComment(new_comment) - nags: Set[int] """ A set of NAGs as integers. NAGs always go behind a move, so the root @@ -818,7 +697,7 @@ def __init__(self, parent: GameNode, move: chess.Move, *, comment: Union[str, li self.parent.variations.append(self) self.nags.update(nags) - self.starting_comments = GameNodeComment(starting_comment) + self.starting_comments = _standardize_comments(starting_comment) def board(self) -> chess.Board: stack: List[chess.Move] = [] @@ -874,7 +753,7 @@ def end(self) -> ChildNode: return typing.cast(ChildNode, super().end()) def _accept_node(self, parent_board: chess.Board, visitor: BaseVisitor[ResultT]) -> None: - if self.starting_comment: + if self.starting_comments: visitor.visit_comment(self.starting_comments) visitor.visit_move(parent_board, self.move) @@ -1270,7 +1149,7 @@ def visit_board(self, board: chess.Board) -> None: """ pass - def visit_comment(self, comment: GameNodeComment) -> None: + def visit_comment(self, comment: list[str]) -> None: """Called for each comment.""" pass @@ -1324,7 +1203,7 @@ def begin_game(self) -> None: self.game: GameT = self.Game() self.variation_stack: List[GameNode] = [self.game] - self.starting_comment = GameNodeComment() + self.starting_comments: list[str] = [] self.in_variation = False def begin_headers(self) -> Headers: @@ -1349,24 +1228,23 @@ def visit_result(self, result: str) -> None: if self.game.headers.get("Result", "*") == "*": self.game.headers["Result"] = result - def visit_comment(self, comment: GameNodeComment) -> None: + def visit_comment(self, comment: Union[str, list[str]]) -> None: + comments = _standardize_comments(comment) if self.in_variation or (self.variation_stack[-1].parent is None and self.variation_stack[-1].is_end()): # Add as a comment for the current node if in the middle of # a variation. Add as a comment for the game if the comment # starts before any move. - new_comments = self.variation_stack[-1].comments + comment - new_comments.remove_empty() - self.variation_stack[-1].comments = new_comments + self.variation_stack[-1].comments.extend(comments) + self.variation_stack[-1].comments = list(filter(None, self.variation_stack[-1].comments)) else: # Otherwise, it is a starting comment. - new_comments = self.starting_comment + comment - new_comments.remove_empty() - self.starting_comment = new_comments + self.starting_comments.extend(comments) + self.starting_comments = list(filter(None, self.starting_comments)) def visit_move(self, board: chess.Board, move: chess.Move) -> None: self.variation_stack[-1] = self.variation_stack[-1].add_variation(move) - self.variation_stack[-1].starting_comments = self.starting_comment - self.starting_comment = GameNodeComment() + self.variation_stack[-1].starting_comments = self.starting_comments + self.starting_comments = [] self.in_variation = True def handle_error(self, error: Exception) -> None: @@ -1534,9 +1412,15 @@ def end_variation(self) -> None: self.write_token(") ") self.force_movenumber = True - def visit_comment(self, comment: GameNodeComment) -> None: + def visit_comment(self, comment: Union[str, list[str]]) -> None: if self.comments and (self.variations or not self.variation_depth): - self.write_token(comment.pgn_format() + " ") + def pgn_format(comments: list[str]) -> str: + comments = map(lambda s: s.replace("{", ""), comments) + comments = map(lambda s: s.replace("}", ""), comments) + return " ".join(f"{{ {comment} }}" for comment in comments if comment) + + comments = _standardize_comments(comment) + self.write_token(pgn_format(comments) + " ") self.force_movenumber = True def visit_nag(self, nag: int) -> None: diff --git a/test.py b/test.py index 4baf2dc58..92afd8de7 100755 --- a/test.py +++ b/test.py @@ -2083,29 +2083,29 @@ class PgnTestCase(unittest.TestCase): def test_exporter(self): game = chess.pgn.Game() - game.comment = "Test game:" + game.comments = ["Test game:"] game.headers["Result"] = "*" game.headers["VeryLongHeader"] = "This is a very long header, much wider than the 80 columns that PGNs are formatted with by default" e4 = game.add_variation(game.board().parse_san("e4")) - e4.comment = "Scandinavian Defense:" + e4.comments = ["Scandinavian Defense:"] e4_d5 = e4.add_variation(e4.board().parse_san("d5")) e4_h5 = e4.add_variation(e4.board().parse_san("h5")) e4_h5.nags.add(chess.pgn.NAG_MISTAKE) - e4_h5.starting_comment = "This" - e4_h5.comment = "is nonsense" + e4_h5.starting_comments = ["This"] + e4_h5.comments = ["is nonsense"] e4_e5 = e4.add_variation(e4.board().parse_san("e5")) e4_e5_Qf3 = e4_e5.add_variation(e4_e5.board().parse_san("Qf3")) e4_e5_Qf3.nags.add(chess.pgn.NAG_MISTAKE) e4_c5 = e4.add_variation(e4.board().parse_san("c5")) - e4_c5.comment = "Sicilian" + e4_c5.comments = ["Sicilian"] e4_d5_exd5 = e4_d5.add_main_variation(e4_d5.board().parse_san("exd5")) - e4_d5_exd5.comment = ["Best", "and the end of this example"] + e4_d5_exd5.comments = ["Best", "and the end of this example"] # Test string exporter with various options. exporter = chess.pgn.StringExporter(headers=False, comments=False, variations=False) @@ -2224,26 +2224,16 @@ def test_read_game_with_multicomment_move(self): pgn = io.StringIO("1. e4 {A common opening} 1... e5 {A common response} {An uncommon comment}") game = chess.pgn.read_game(pgn) first_move = game.variation(0) - self.assertEqual(first_move.comment, "A common opening") - self.assertEqual(first_move.comments, "A common opening") self.assertEqual(first_move.comments, ["A common opening"]) second_move = first_move.variation(0) self.assertEqual(second_move.comments, ["A common response", "An uncommon comment"]) second_move.comments.pop() self.assertEqual(second_move.comments, ["A common response"]) - self.assertEqual(second_move.comments, "A common response") - self.assertEqual(second_move.comment, "A common response") second_move.comments.append("A replaced comment") multiple_comments = ["A common response", "A replaced comment"] self.assertEqual(second_move.comments, multiple_comments) - for move_comment, test_comment in zip(second_move.comments, multiple_comments): - self.assertEqual(move_comment, test_comment) - self.assertEqual(list(second_move.comments), multiple_comments) - self.assertEqual(second_move.comment, " ".join(multiple_comments)) second_move.comments.pop(0) self.assertEqual(second_move.comments, ["A replaced comment"]) - self.assertEqual(second_move.comments, "A replaced comment") - self.assertEqual(second_move.comment, "A replaced comment") def test_comment_at_eol(self): pgn = io.StringIO(textwrap.dedent("""\ @@ -2259,7 +2249,7 @@ def test_comment_at_eol(self): # Make sure the comment for the second variation is there. self.assertIn(5, node[1].nags) - self.assertEqual(node[1].comment, "\n/\\ Ne7, c6") + self.assertEqual(node[1].comments, ["\n/\\ Ne7, c6"]) def test_promotion_without_equals(self): # Example game from https://github.com/rozim/ChessData as originally @@ -2347,12 +2337,12 @@ def test_variation_stack(self): def test_game_starting_comment(self): pgn = io.StringIO("{ Game starting comment } 1. d3") game = chess.pgn.read_game(pgn) - self.assertEqual(game.comment, "Game starting comment") + self.assertEqual(game.comments, ["Game starting comment"]) self.assertEqual(game[0].san(), "d3") pgn = io.StringIO("{ Empty game, but has a comment }") game = chess.pgn.read_game(pgn) - self.assertEqual(game.comment, "Empty game, but has a comment") + self.assertEqual(game.comments, ["Empty game, but has a comment"]) def test_game_starting_variation(self): pgn = io.StringIO(textwrap.dedent("""\ @@ -2360,17 +2350,17 @@ def test_game_starting_variation(self): """)) game = chess.pgn.read_game(pgn) - self.assertEqual(game.comment, "Start of game") + self.assertEqual(game.comments, ["Start of game"]) node = game[0] self.assertEqual(node.move, chess.Move.from_uci("e2e4")) - self.assertFalse(node.comment) - self.assertFalse(node.starting_comment) + self.assertFalse(node.comments) + self.assertFalse(node.starting_comments) node = game[1] self.assertEqual(node.move, chess.Move.from_uci("d2d4")) - self.assertFalse(node.comment) - self.assertEqual(node.starting_comment, "Start of variation") + self.assertFalse(node.comments) + self.assertEqual(node.starting_comments, ["Start of variation"]) def test_annotation_symbols(self): pgn = io.StringIO("1. b4?! g6 2. Bb2 Nc6? 3. Bxh8!!") @@ -2715,12 +2705,12 @@ def test_add_line(self): tail = game.add_line(moves, starting_comment="start", comment="end", nags=(17, 42)) self.assertEqual(tail.parent.move, chess.Move.from_uci("g1f3")) - self.assertEqual(tail.parent.starting_comment, "start") - self.assertEqual(tail.parent.comment, "") + self.assertEqual(tail.parent.starting_comments, ["start"]) + self.assertEqual(tail.parent.comments, []) self.assertEqual(len(tail.parent.nags), 0) self.assertEqual(tail.move, chess.Move.from_uci("d7d5")) - self.assertEqual(tail.comment, "end") + self.assertEqual(tail.comments, ["end"]) self.assertIn(42, tail.nags) def test_mainline(self): @@ -2850,12 +2840,11 @@ def test_recursion(self): def test_annotations(self): game = chess.pgn.Game() - game.comment = "foo [%bar] baz" + game.comments = ["foo [%bar] baz"] self.assertTrue(game.clock() is None) clock = 12345 game.set_clock(clock) - self.assertEqual(game.comment, "foo [%bar] baz [%clk 3:25:45]") self.assertEqual(game.comments, ["foo [%bar] baz", "[%clk 3:25:45]"]) self.assertEqual(game.clock(), clock) @@ -2872,7 +2861,6 @@ def test_annotations(self): self.assertEqual(game.arrows(), []) game.set_arrows([(chess.A1, chess.A1), chess.svg.Arrow(chess.A1, chess.H1, color="red"), chess.svg.Arrow(chess.B1, chess.B8)]) self.assertEqual(game.comments, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]", "[%eval #1,5]"]) - self.assertEqual(game.comment, "[%csl Ga1][%cal Ra1h1,Gb1b8] foo [%bar] baz [%clk 3:25:45] [%eval #1,5]") arrows = game.arrows() self.assertEqual(len(arrows), 3) self.assertEqual(arrows[0].color, "green") @@ -2883,21 +2871,17 @@ def test_annotations(self): emt = 321 game.set_emt(emt) self.assertEqual(game.comments, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]", "[%eval #1,5]", "[%emt 0:05:21]"]) - self.assertEqual(game.comment, "[%csl Ga1][%cal Ra1h1,Gb1b8] foo [%bar] baz [%clk 3:25:45] [%eval #1,5] [%emt 0:05:21]") self.assertEqual(game.emt(), emt) game.set_eval(None) self.assertEqual(game.comments, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]", "[%emt 0:05:21]"]) - self.assertEqual(game.comment, "[%csl Ga1][%cal Ra1h1,Gb1b8] foo [%bar] baz [%clk 3:25:45] [%emt 0:05:21]") game.set_emt(None) self.assertEqual(game.comments, ["[%csl Ga1][%cal Ra1h1,Gb1b8]", "foo [%bar] baz", "[%clk 3:25:45]"]) - self.assertEqual(game.comment, "[%csl Ga1][%cal Ra1h1,Gb1b8] foo [%bar] baz [%clk 3:25:45]") game.set_clock(None) game.set_arrows([]) - self.assertEqual(game.comments, "foo [%bar] baz") - self.assertEqual(game.comment, "foo [%bar] baz") + self.assertEqual(game.comments, ["foo [%bar] baz"]) def test_eval(self): game = chess.pgn.Game() @@ -2907,28 +2891,28 @@ def test_eval(self): def test_float_emt(self): game = chess.pgn.Game() - game.comment = "[%emt 0:00:01.234]" + game.comments = ["[%emt 0:00:01.234]"] self.assertEqual(game.emt(), 1.234) game.set_emt(6.54321) - self.assertEqual(game.comment, "[%emt 0:00:06.543]") + self.assertEqual(game.comments, ["[%emt 0:00:06.543]"]) self.assertEqual(game.emt(), 6.543) game.set_emt(-70) - self.assertEqual(game.comment, "[%emt 0:00:00]") # Clamped + self.assertEqual(game.comments, ["[%emt 0:00:00]"]) # Clamped self.assertEqual(game.emt(), 0) def test_float_clk(self): game = chess.pgn.Game() - game.comment = "[%clk 0:00:01.234]" + game.comments = ["[%clk 0:00:01.234]"] self.assertEqual(game.clock(), 1.234) game.set_clock(6.54321) - self.assertEqual(game.comment, "[%clk 0:00:06.543]") + self.assertEqual(game.comments, ["[%clk 0:00:06.543]"]) self.assertEqual(game.clock(), 6.543) game.set_clock(-70) - self.assertEqual(game.comment, "[%clk 0:00:00]") # Clamped + self.assertEqual(game.comments, ["[%clk 0:00:00]"]) # Clamped self.assertEqual(game.clock(), 0) def test_node_turn(self): From 52dc00154d10c230c0aa6ce13299d7a0c825f19a Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Sun, 26 May 2024 15:04:05 -0700 Subject: [PATCH 032/116] Convert map return type to list --- chess/pgn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index b40ce257c..b999d79a6 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -1415,8 +1415,8 @@ def end_variation(self) -> None: def visit_comment(self, comment: Union[str, list[str]]) -> None: if self.comments and (self.variations or not self.variation_depth): def pgn_format(comments: list[str]) -> str: - comments = map(lambda s: s.replace("{", ""), comments) - comments = map(lambda s: s.replace("}", ""), comments) + comments = list(map(lambda s: s.replace("{", ""), comments)) + comments = list(map(lambda s: s.replace("}", ""), comments)) return " ".join(f"{{ {comment} }}" for comment in comments if comment) comments = _standardize_comments(comment) From 3b23452ee5728d1ec75e2cbf6f1c208e0416a9ee Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Sun, 26 May 2024 15:14:13 -0700 Subject: [PATCH 033/116] Remove tests of python list methods --- test.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test.py b/test.py index 92afd8de7..cf5439547 100755 --- a/test.py +++ b/test.py @@ -2227,13 +2227,6 @@ def test_read_game_with_multicomment_move(self): self.assertEqual(first_move.comments, ["A common opening"]) second_move = first_move.variation(0) self.assertEqual(second_move.comments, ["A common response", "An uncommon comment"]) - second_move.comments.pop() - self.assertEqual(second_move.comments, ["A common response"]) - second_move.comments.append("A replaced comment") - multiple_comments = ["A common response", "A replaced comment"] - self.assertEqual(second_move.comments, multiple_comments) - second_move.comments.pop(0) - self.assertEqual(second_move.comments, ["A replaced comment"]) def test_comment_at_eol(self): pgn = io.StringIO(textwrap.dedent("""\ From 3829d262c325b61b0004a314347f985008a9eb84 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Mon, 17 Jun 2024 21:18:43 +0200 Subject: [PATCH 034/116] Factor out Tablebase.add_file() --- chess/syzygy.py | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/chess/syzygy.py b/chess/syzygy.py index e1fe07eb7..0c7d41500 100644 --- a/chess/syzygy.py +++ b/chess/syzygy.py @@ -1511,27 +1511,23 @@ def add_directory(self, directory: str, *, load_wdl: bool = True, load_dtz: bool Returns the number of table files that were found. """ - num = 0 directory = os.path.abspath(directory) - - for filename in os.listdir(directory): - path = os.path.join(directory, filename) - tablename, ext = os.path.splitext(filename) - - if is_tablename(tablename, one_king=self.variant.one_king) and os.path.isfile(path): - if load_wdl: - if ext == self.variant.tbw_suffix: - num += self._open_table(self.wdl, WdlTable, path) - elif "P" not in tablename and ext == self.variant.pawnless_tbw_suffix: - num += self._open_table(self.wdl, WdlTable, path) - - if load_dtz: - if ext == self.variant.tbz_suffix: - num += self._open_table(self.dtz, DtzTable, path) - elif "P" not in tablename and ext == self.variant.pawnless_tbz_suffix: - num += self._open_table(self.dtz, DtzTable, path) - - return num + return sum(self.add_file(os.path.join(directory, filename), load_wdl=load_wdl, load_dtz=load_dtz) for filename in os.listdir(directory)) + + def add_file(self, path: str, *, load_wdl: bool = True, load_dtz: bool = True) -> int: + tablename, ext = os.path.splitext(os.path.basename(path)) + if is_tablename(tablename, one_king=self.variant.one_king) and os.path.isfile(path): + if load_wdl: + if ext == self.variant.tbw_suffix: + return self._open_table(self.wdl, WdlTable, path) + elif "P" not in tablename and ext == self.variant.pawnless_tbw_suffix: + return self._open_table(self.wdl, WdlTable, path) + if load_dtz: + if ext == self.variant.tbz_suffix: + return self._open_table(self.dtz, DtzTable, path) + elif "P" not in tablename and ext == self.variant.pawnless_tbz_suffix: + return self._open_table(self.dtz, DtzTable, path) + return 0 def probe_wdl_table(self, board: chess.Board) -> int: # Test for variant end. From a41c3c88022fd97ab0eaf7b032a3158b69628b66 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Thu, 4 Jul 2024 16:41:19 +0200 Subject: [PATCH 035/116] Do not eagerly check syzygy max pieces (fixes #1093) --- chess/syzygy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chess/syzygy.py b/chess/syzygy.py index 0c7d41500..807b0922e 100644 --- a/chess/syzygy.py +++ b/chess/syzygy.py @@ -1546,6 +1546,8 @@ def probe_wdl_table(self, board: chess.Board) -> int: try: table = typing.cast(WdlTable, self.wdl[key]) except KeyError: + if chess.popcount(board.occupied) > TBPIECES: + raise KeyError(f"syzygy tables support up to {TBPIECES} pieces, not {chess.popcount(board.occupied)}: {board.fen()}") raise MissingTableError(f"did not find wdl table {key}") self._bump_lru(table) @@ -1558,8 +1560,6 @@ def probe_ab(self, board: chess.Board, alpha: int, beta: int, threats: bool = Fa raise KeyError(f"tablebase has been opened for {self.variant.uci_variant}, probed with: {board.uci_variant}") if board.castling_rights: raise KeyError(f"syzygy tables do not contain positions with castling rights: {board.fen()}") - if chess.popcount(board.occupied) > TBPIECES: - raise KeyError(f"syzygy tables support up to {TBPIECES} pieces, not {chess.popcount(board.occupied)}: {board.fen()}") # Special case: Variant with compulsory captures. if self.variant.captures_compulsory: From 95803fc64a89307bfe51910f9e9349d120b81705 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Thu, 4 Jul 2024 20:58:32 +0200 Subject: [PATCH 036/116] Artificially limit syzygy ab search after relaxing bound --- chess/syzygy.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/chess/syzygy.py b/chess/syzygy.py index 807b0922e..8ba1cbca4 100644 --- a/chess/syzygy.py +++ b/chess/syzygy.py @@ -1561,6 +1561,13 @@ def probe_ab(self, board: chess.Board, alpha: int, beta: int, threats: bool = Fa if board.castling_rights: raise KeyError(f"syzygy tables do not contain positions with castling rights: {board.fen()}") + # Probing resolves captures, so sometimes we can obtain results for + # positions that have more pieces than the maximum number of supported + # pieces. We artificially limit this to one additional level, to + # make sure search remains somewhat bounded. + if chess.popcount(board.occupied) > TBPIECES + 1: + raise KeyError(f"syzygy tables support up to {TBPIECES} pieces, not {chess.popcount(board.occupied)}: {board.fen()}") + # Special case: Variant with compulsory captures. if self.variant.captures_compulsory: if board.is_variant_win(): From 474c87bfcccb8e2aa78a4a94414af3b747340f31 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 10 Jul 2024 18:02:09 +0200 Subject: [PATCH 037/116] Add another SAN disambiguation test (niklasf/shakmaty#77) --- test.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test.py b/test.py index 4ca3be6c2..00d766a03 100755 --- a/test.py +++ b/test.py @@ -703,6 +703,11 @@ def test_san(self): self.assertEqual(board.san(chess.Move.from_uci("h4g6")), "Nh4g6") self.assertEqual(board.fen(), fen) + # Test a bug where shakmaty used overly specific disambiguation. + fen = "8/2KN1p2/5p2/3N1B1k/5PNp/7P/7P/8 w - -" + board = chess.Board(fen) + self.assertEqual(board.san(chess.Move.from_uci("d5f6")), "N5xf6#") + # Do not disambiguate illegal alternatives. fen = "8/8/8/R2nkn2/8/8/2K5/8 b - - 0 1" board = chess.Board(fen) From ec8ecec5c0277691ec76041e82bf13140f7f719a Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Thu, 11 Jul 2024 16:03:22 +0200 Subject: [PATCH 038/116] chess-playground discontinued --- README.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/README.rst b/README.rst index c80957a37..a6c3185c6 100644 --- a/README.rst +++ b/README.rst @@ -350,7 +350,6 @@ If you like, share interesting things you are using python-chess for, for exampl * Django Rest Framework API for multiplayer chess – https://github.com/WorkShoft/capablanca-api * a `browser based PGN viewer `_ written in PyScript – https://github.com/nmstoker/ChessMatchViewer * an accessible chessboard that allows blind and visually impaired players to play chess against Stockfish – https://github.com/blindpandas/chessmart -* analyse games to extract various statistics – https://github.com/mvrozanti/chess-playground * a web-based chess vision exercise – https://github.com/3d12/rookognition From 63aac2ec65860a64256a190220b793e80b132b50 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 19 Jul 2024 16:31:22 +0200 Subject: [PATCH 039/116] Expose optional occupied parameter for attackers_mask (#1090) --- chess/__init__.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/chess/__init__.py b/chess/__init__.py index bed9eec5d..a90df9ef6 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -902,7 +902,9 @@ def attacks(self, square: Square) -> SquareSet: """ return SquareSet(self.attacks_mask(square)) - def _attackers_mask(self, color: Color, square: Square, occupied: Bitboard) -> Bitboard: + def attackers_mask(self, color: Color, square: Square, occupied: Optional[Bitboard] = None) -> Bitboard: + occupied = self.occupied if occupied is None else occupied + rank_pieces = BB_RANK_MASKS[square] & occupied file_pieces = BB_FILE_MASKS[square] & occupied diag_pieces = BB_DIAG_MASKS[square] & occupied @@ -920,27 +922,38 @@ def _attackers_mask(self, color: Color, square: Square, occupied: Bitboard) -> B return attackers & self.occupied_co[color] - def attackers_mask(self, color: Color, square: Square) -> Bitboard: - return self._attackers_mask(color, square, self.occupied) - - def is_attacked_by(self, color: Color, square: Square) -> bool: + def is_attacked_by(self, color: Color, square: Square, occupied: Optional[IntoSquareSet] = None) -> bool: """ Checks if the given side attacks the given square. Pinned pieces still count as attackers. Pawns that can be captured en passant are **not** considered attacked. + + *occupied* determines which squares are considered to block attacks. + For example, + ``board.occupied ^ board.pieces_mask(chess.KING, board.turn)`` can be + used to consider X-ray attacks through the king. + Defaults to ``board.occupied`` (all pieces including the king, + no X-ray attacks). """ - return bool(self.attackers_mask(color, square)) + return bool(self.attackers_mask(color, square, None if occupied is None else SquareSet(occupied).mask)) - def attackers(self, color: Color, square: Square) -> SquareSet: + def attackers(self, color: Color, square: Square, occupied: Optional[IntoSquareSet] = None) -> SquareSet: """ Gets the set of attackers of the given color for the given square. Pinned pieces still count as attackers. + *occupied* determines which squares are considered to block attacks. + For example, + ``board.occupied ^ board.pieces_mask(chess.KING, board.turn)`` can be + used to consider X-ray attacks through the king. + Defaults to ``board.occupied`` (all pieces including the king, + no X-ray attacks). + Returns a :class:`set of squares `. """ - return SquareSet(self.attackers_mask(color, square)) + return SquareSet(self.attackers_mask(color, square, None if occupied is None else SquareSet(occupied).mask)) def pin_mask(self, color: Color, square: Square) -> Bitboard: king = self.king(color) @@ -3723,7 +3736,7 @@ def generate_legal_captures(self, from_mask: Bitboard = BB_ALL, to_mask: Bitboar self.generate_legal_ep(from_mask, to_mask)) def _attacked_for_king(self, path: Bitboard, occupied: Bitboard) -> bool: - return any(self._attackers_mask(not self.turn, sq, occupied) for sq in scan_reversed(path)) + return any(self.attackers_mask(not self.turn, sq, occupied) for sq in scan_reversed(path)) def generate_castling_moves(self, from_mask: Bitboard = BB_ALL, to_mask: Bitboard = BB_ALL) -> Iterator[Move]: if self.is_variant_end(): From 7836d446bf0cfc3b265e038fa381a7e52a543c41 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 19 Jul 2024 16:41:25 +0200 Subject: [PATCH 040/116] Add chess.svg.SvgWrapper._repr_html_() (closes #1096) --- chess/svg.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/chess/svg.py b/chess/svg.py index d3d19e89e..2948b0b80 100644 --- a/chess/svg.py +++ b/chess/svg.py @@ -144,6 +144,9 @@ class SvgWrapper(str): def _repr_svg_(self) -> SvgWrapper: return self + def _repr_html_(self) -> SvgWrapper: + return self + def _svg(viewbox: int, size: Optional[int]) -> ET.Element: svg = ET.Element("svg", { From 247d8a06a6b2c2dc06f9ce0b7f3e94dbd9b816bd Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 19 Jul 2024 17:27:12 +0200 Subject: [PATCH 041/116] Update changelog for soon to be released final 1.x --- CHANGELOG.rst | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0fa76effa..a4534badb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,9 +6,26 @@ Upcoming in the next release Changes: -* Dropped support for Python 3.7, which had reached its end of life. -* `chess.engine.EventLoopPolicy` is no longer needed and now merely an alias +* Drop support for Python 3.7, which has reached its end of life. +* ``chess.engine.EventLoopPolicy`` is no longer needed and now merely an alias for the default event loop policy. +* If available and requested via ``setpgrp``, use ``process_group`` support + from Python 3.11 for engine processes. +* No longer eagerly reject 8 piece positions in ``chess.syzygy``, so that + some 8 piece positions with decisive captures can be probed successfully. +* The string wrapper returned by ``chess.svg`` functions now also implements + ``_repr_html_``. + +New features: + +* Add ``chess.pgn.Game.time_control()`` and related data models. +* Add model ``sf16.1`` for ``chess.engine.Score.wdl()``, the new default. + +Bugfixes: + +* Fix handling of whitespace in UCI engine communication. +* For ``chess.Board.epd()`` and ``chess.Board.set_epd()``, require that EPD + opcodes start with a letter. New in v1.10.0 (27th Jul 2023) ------------------------------ From c7199a6354fe3053235c8ad88d79714afccdc0b9 Mon Sep 17 00:00:00 2001 From: Deepyaman Datta Date: Sun, 21 Jul 2024 22:42:48 -0600 Subject: [PATCH 042/116] Update default time control from UNKNOW to UNKNOWN --- chess/pgn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index 55eddbc29..0d0879ae0 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -143,7 +143,7 @@ class SkipType(enum.Enum): class TimeControlType(enum.Enum): - UNKNOW = 0 + UNKNOWN = 0 UNLIMITED = 1 STANDARD = 2 RAPID = 3 @@ -172,7 +172,7 @@ class TimeControl: """ parts: list[TimeControlPart] = dataclasses.field(default_factory=list) - type: TimeControlType = TimeControlType.UNKNOW + type: TimeControlType = TimeControlType.UNKNOWN class _AcceptFrame: From ad6f756c4406d66a59a9cd1050f50137de84dc30 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Wed, 31 Jul 2024 00:04:57 -0700 Subject: [PATCH 043/116] Factor out repeated annotation editing code --- chess/pgn.py | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index b999d79a6..b8cfbe440 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -491,16 +491,7 @@ def set_eval(self, score: Optional[chess.engine.PovScore], depth: Optional[int] elif score.white().mate(): eval = f"[%eval #{score.white().mate()}{depth_suffix}]" - found = 0 - for index in range(len(self.comments)): - self.comments[index], found = EVAL_REGEX.subn(_condense_affix(eval), self.comments[index], count=1) - if found: - break - - self.comments = list(filter(None, self.comments)) - - if not found and eval: - self.comments.append(eval) + self._replace_or_add_annotation(eval, EVAL_REGEX) def arrows(self) -> List[chess.svg.Arrow]: """ @@ -573,16 +564,7 @@ def set_clock(self, seconds: Optional[float]) -> None: seconds_part = f"{seconds:06.3f}".rstrip("0").rstrip(".") clk = f"[%clk {hours:d}:{minutes:02d}:{seconds_part}]" - found = 0 - for index in range(len(self.comments)): - self.comments[index], found = CLOCK_REGEX.subn(_condense_affix(clk), self.comments[index], count=1) - if found: - break - - self.comments = list(filter(None, self.comments)) - - if not found and clk: - self.comments.append(clk) + self._replace_or_add_annotation(clk, CLOCK_REGEX) def emt(self) -> Optional[float]: """ @@ -611,16 +593,19 @@ def set_emt(self, seconds: Optional[float]) -> None: seconds_part = f"{seconds:06.3f}".rstrip("0").rstrip(".") emt = f"[%emt {hours:d}:{minutes:02d}:{seconds_part}]" + self._replace_or_add_annotation(emt, EMT_REGEX) + + def _replace_or_add_annotation(self, text: str, regex: re.Pattern[str]) -> None: found = 0 for index in range(len(self.comments)): - self.comments[index], found = EMT_REGEX.subn(_condense_affix(emt), self.comments[index], count=1) + self.comments[index], found = regex.subn(_condense_affix(text), self.comments[index], count=1) if found: break self.comments = list(filter(None, self.comments)) - if not found and emt: - self.comments.append(emt) + if not found and text: + self.comments.append(text) @abc.abstractmethod def accept(self, visitor: BaseVisitor[ResultT]) -> ResultT: From 744d2fcae0b2c9d26007648099094b05475bb5cc Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Wed, 31 Jul 2024 00:48:02 -0700 Subject: [PATCH 044/116] Lazily remove curly braces from comments --- chess/pgn.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index c7d4a87a3..3acf66401 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -1400,9 +1400,8 @@ def end_variation(self) -> None: def visit_comment(self, comment: Union[str, list[str]]) -> None: if self.comments and (self.variations or not self.variation_depth): def pgn_format(comments: list[str]) -> str: - comments = list(map(lambda s: s.replace("{", ""), comments)) - comments = list(map(lambda s: s.replace("}", ""), comments)) - return " ".join(f"{{ {comment} }}" for comment in comments if comment) + edit = map(lambda s: s.replace("{", "").replace("}", ""), comments) + return " ".join(f"{{ {comment} }}" for comment in edit if comment) comments = _standardize_comments(comment) self.write_token(pgn_format(comments) + " ") From dce369c6388805b5ac51c8b9b765e4da33f09795 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Wed, 31 Jul 2024 00:48:28 -0700 Subject: [PATCH 045/116] Add test for removing curly braces from comments --- test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.py b/test.py index 23ecab2fc..2a4f551ea 100755 --- a/test.py +++ b/test.py @@ -2110,7 +2110,7 @@ def test_exporter(self): e4_c5.comments = ["Sicilian"] e4_d5_exd5 = e4_d5.add_main_variation(e4_d5.board().parse_san("exd5")) - e4_d5_exd5.comments = ["Best", "and the end of this example"] + e4_d5_exd5.comments = ["Best", "and the end of this {example}"] # Test string exporter with various options. exporter = chess.pgn.StringExporter(headers=False, comments=False, variations=False) From dda9e8fedc75eefb0255245abff0782ecb26f882 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 31 Jul 2024 15:09:53 +0200 Subject: [PATCH 046/116] Refactor chess.engine.BaseCommand to pass stricter type checking --- chess/engine.py | 614 +++++++++++++++++++++++++++--------------------- 1 file changed, 344 insertions(+), 270 deletions(-) diff --git a/chess/engine.py b/chess/engine.py index c2b470dd5..d1bbaaee1 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -24,6 +24,9 @@ from types import TracebackType from typing import Any, Callable, Coroutine, Deque, Dict, Generator, Generic, Iterable, Iterator, List, Literal, Mapping, MutableMapping, Optional, Tuple, Type, TypedDict, TypeVar, Union +if typing.TYPE_CHECKING: + from typing_extensions import Self + WdlModel = Literal["sf", "sf16.1", "sf16", "sf15.1", "sf15", "sf14", "sf12", "lichess"] @@ -895,7 +898,7 @@ class Protocol(asyncio.SubprocessProtocol, metaclass=abc.ABCMeta): returncode: asyncio.Future[int] """Future: Exit code of the process.""" - def __init__(self: ProtocolT) -> None: + def __init__(self) -> None: self.loop = asyncio.get_running_loop() self.transport: Optional[asyncio.SubprocessTransport] = None @@ -904,8 +907,8 @@ def __init__(self: ProtocolT) -> None: 2: bytearray(), # stderr } - self.command: Optional[BaseCommand[ProtocolT, Any]] = None - self.next_command: Optional[BaseCommand[ProtocolT, Any]] = None + self.command: Optional[BaseCommand[Any]] = None + self.next_command: Optional[BaseCommand[Any]] = None self.initialized = False self.returncode: asyncio.Future[int] = asyncio.Future() @@ -915,7 +918,7 @@ def connection_made(self, transport: asyncio.BaseTransport) -> None: self.transport = transport # type: ignore LOGGER.debug("%s: Connection made", self) - def connection_lost(self: ProtocolT, exc: Optional[Exception]) -> None: + def connection_lost(self, exc: Optional[Exception]) -> None: assert self.transport is not None code = self.transport.get_returncode() assert code is not None, "connect lost, but got no returncode" @@ -923,10 +926,10 @@ def connection_lost(self: ProtocolT, exc: Optional[Exception]) -> None: # Terminate commands. if self.command is not None: - self.command._engine_terminated(self, code) + self.command._engine_terminated(code) self.command = None if self.next_command is not None: - self.next_command._engine_terminated(self, code) + self.next_command._engine_terminated(code) self.next_command = None self.returncode.set_result(code) @@ -960,18 +963,18 @@ def pipe_data_received(self, fd: int, data: Union[bytes, str]) -> None: def error_line_received(self, line: str) -> None: LOGGER.warning("%s: stderr >> %s", self, line) - def _line_received(self: ProtocolT, line: str) -> None: + def _line_received(self: Protocol, line: str) -> None: LOGGER.debug("%s: >> %s", self, line) self.line_received(line) if self.command: - self.command._line_received(self, line) + self.command._line_received(line) def line_received(self, line: str) -> None: pass - async def communicate(self: ProtocolT, command_factory: Callable[[ProtocolT], BaseCommand[ProtocolT, T]]) -> T: + async def communicate(self, command_factory: Callable[[Self], BaseCommand[T]]) -> T: command = command_factory(self) if self.returncode.done(): @@ -993,18 +996,18 @@ def previous_command_finished(_: Optional[asyncio.Future[None]]) -> None: def cancel_if_cancelled(result: asyncio.Future[T]) -> None: if result.cancelled(): - cmd._cancel(self) + cmd._cancel() cmd.result.add_done_callback(cancel_if_cancelled) cmd.finished.add_done_callback(previous_command_finished) - cmd._start(self) + cmd._start() if self.command is None: previous_command_finished(None) elif not self.command.result.done(): self.command.result.cancel() elif not self.command.result.cancelled(): - self.command._cancel(self) + self.command._cancel() return await command.result @@ -1207,32 +1210,34 @@ class CommandState(enum.Enum): DONE = enum.auto() -class BaseCommand(Generic[ProtocolT, T]): - def __init__(self, engine: ProtocolT) -> None: +class BaseCommand(Generic[T]): + def __init__(self, engine: Protocol) -> None: + self._engine = engine + self.state = CommandState.NEW self.result: asyncio.Future[T] = asyncio.Future() self.finished: asyncio.Future[None] = asyncio.Future() - def _engine_terminated(self, engine: ProtocolT, code: int) -> None: + def _engine_terminated(self, code: int) -> None: hint = ", binary not compatible with cpu?" if code in [-4, 0xc000001d] else "" exc = EngineTerminatedError(f"engine process died unexpectedly (exit code: {code}{hint})") if self.state == CommandState.ACTIVE: - self.engine_terminated(engine, exc) + self.engine_terminated(exc) elif self.state == CommandState.CANCELLING: self.finished.set_result(None) elif self.state == CommandState.NEW: - self._handle_exception(engine, exc) + self._handle_exception(exc) - def _handle_exception(self, engine: ProtocolT, exc: Exception) -> None: + def _handle_exception(self, exc: Exception) -> None: if not self.result.done(): self.result.set_exception(exc) else: - engine.loop.call_exception_handler({ + self._engine.loop.call_exception_handler({ # XXX "message": f"{type(self).__name__} failed after returning preliminary result ({self.result!r})", "exception": exc, - "protocol": engine, - "transport": engine.transport, + "protocol": self._engine, + "transport": self._engine.transport, }) if not self.finished.done(): @@ -1245,43 +1250,43 @@ def set_finished(self) -> None: self.finished.set_result(None) self.state = CommandState.DONE - def _cancel(self, engine: ProtocolT) -> None: + def _cancel(self) -> None: if self.state != CommandState.CANCELLING and self.state != CommandState.DONE: assert self.state == CommandState.ACTIVE self.state = CommandState.CANCELLING - self.cancel(engine) + self.cancel() - def _start(self, engine: ProtocolT) -> None: + def _start(self) -> None: assert self.state == CommandState.NEW self.state = CommandState.ACTIVE try: - self.check_initialized(engine) - self.start(engine) + self.check_initialized() + self.start() except EngineError as err: - self._handle_exception(engine, err) + self._handle_exception(err) - def _line_received(self, engine: ProtocolT, line: str) -> None: + def _line_received(self, line: str) -> None: assert self.state in [CommandState.ACTIVE, CommandState.CANCELLING] try: - self.line_received(engine, line) + self.line_received(line) except EngineError as err: - self._handle_exception(engine, err) + self._handle_exception(err) - def cancel(self, engine: ProtocolT) -> None: + def cancel(self) -> None: pass - def check_initialized(self, engine: ProtocolT) -> None: - if not engine.initialized: + def check_initialized(self) -> None: + if not self._engine.initialized: raise EngineError("tried to run command, but engine is not initialized") - def start(self, engine: ProtocolT) -> None: + def start(self) -> None: raise NotImplementedError - def line_received(self, engine: ProtocolT, line: str) -> None: + def line_received(self, line: str) -> None: pass - def engine_terminated(self, engine: ProtocolT, exc: Exception) -> None: - self._handle_exception(engine, exc) + def engine_terminated(self, exc: Exception) -> None: + self._handle_exception(exc) def __repr__(self) -> str: return "<{} at {:#x} (state={}, result={}, finished={}>".format(type(self).__name__, id(self), self.state, self.result, self.finished) @@ -1307,26 +1312,33 @@ def __init__(self) -> None: self.ponderhit = False async def initialize(self) -> None: - class UciInitializeCommand(BaseCommand[UciProtocol, None]): - def check_initialized(self, engine: UciProtocol) -> None: - if engine.initialized: + class UciInitializeCommand(BaseCommand[None]): + def __init__(self, engine: UciProtocol): + super().__init__(engine) + self.engine = engine + + @typing.override + def check_initialized(self) -> None: + if self.engine.initialized: raise EngineError("engine already initialized") - def start(self, engine: UciProtocol) -> None: - engine.send_line("uci") + @typing.override + def start(self) -> None: + self.engine.send_line("uci") - def line_received(self, engine: UciProtocol, line: str) -> None: + @typing.override + def line_received(self, line: str) -> None: token, remaining = _next_token(line) if line.strip() == "uciok" and not self.result.done(): - engine.initialized = True + self.engine.initialized = True self.result.set_result(None) self.set_finished() elif token == "option": - self._option(engine, remaining) + self._option(remaining) elif token == "id": - self._id(engine, remaining) + self._id(remaining) - def _option(self, engine: UciProtocol, arg: str) -> None: + def _option(self, arg: str) -> None: current_parameter = None option_parts: dict[str, str] = {k: "" for k in ["name", "type", "default", "min", "max"]} var = [] @@ -1357,16 +1369,16 @@ def parse_min_max_value(option_parts: dict[str, str], which: Literal["min", "max without_default = Option(name, type, None, min, max, var) option = Option(without_default.name, without_default.type, without_default.parse(default), min, max, var) - engine.options[option.name] = option + self.engine.options[option.name] = option if option.default is not None: - engine.config[option.name] = option.default + self.engine.config[option.name] = option.default if option.default is not None and not option.is_managed() and option.name.lower() != "uci_analysemode": - engine.target_config[option.name] = option.default + self.engine.target_config[option.name] = option.default - def _id(self, engine: UciProtocol, arg: str) -> None: + def _id(self, arg: str) -> None: key, value = _next_token(arg) - engine.id[key] = value.strip() + self.engine.id[key] = value.strip() return await self.communicate(UciInitializeCommand) @@ -1395,16 +1407,21 @@ def debug(self, on: bool = True) -> None: self.send_line("debug off") async def ping(self) -> None: - class UciPingCommand(BaseCommand[UciProtocol, None]): - def start(self, engine: UciProtocol) -> None: - engine._isready() + class UciPingCommand(BaseCommand[None]): + def __init__(self, engine: UciProtocol) -> None: + super().__init__(engine) + self.engine = engine - def line_received(self, engine: UciProtocol, line: str) -> None: + def start(self) -> None: + self.engine._isready() + + @typing.override + def line_received(self, line: str) -> None: if line.strip() == "readyok": self.result.set_result(None) self.set_finished() else: - LOGGER.warning("%s: Unexpected engine output: %r", engine, line) + LOGGER.warning("%s: Unexpected engine output: %r", self.engine, line) return await self.communicate(UciPingCommand) @@ -1438,10 +1455,14 @@ def _configure(self, options: ConfigMapping) -> None: self._setoption(name, value) async def configure(self, options: ConfigMapping) -> None: - class UciConfigureCommand(BaseCommand[UciProtocol, None]): - def start(self, engine: UciProtocol) -> None: - engine._configure(options) - engine.target_config.update({name: value for name, value in options.items() if value is not None}) + class UciConfigureCommand(BaseCommand[None]): + def __init__(self, engine: UciProtocol): + super().__init__(engine) + self.engine = engine + + def start(self) -> None: + self.engine._configure(options) + self.engine.target_config.update({name: value for name, value in options.items() if value is not None}) self.result.set_result(None) self.set_finished() @@ -1541,77 +1562,82 @@ async def play(self, board: chess.Board, limit: Limit, *, game: object = None, i new_options[name] = value new_options.update(self._opponent_configuration(opponent=opponent)) - class UciPlayCommand(BaseCommand[UciProtocol, PlayResult]): + engine = self + + class UciPlayCommand(BaseCommand[PlayResult]): def __init__(self, engine: UciProtocol): super().__init__(engine) + self.engine = engine # May ponderhit only in the same game and with unchanged target # options. The managed options UCI_AnalyseMode, Ponder, and # MultiPV never change between pondering play commands. engine.may_ponderhit = board if ponder and not engine.first_game and game == engine.game and not engine._changed_options(new_options) else None - def start(self, engine: UciProtocol) -> None: + @typing.override + def start(self) -> None: self.info: InfoDict = {} self.pondering: Optional[chess.Board] = None self.sent_isready = False self.start_time = time.perf_counter() - if engine.ponderhit: - engine.ponderhit = False - engine.send_line("ponderhit") + if self.engine.ponderhit: + self.engine.ponderhit = False + self.engine.send_line("ponderhit") return - if "UCI_AnalyseMode" in engine.options and "UCI_AnalyseMode" not in engine.target_config and all(name.lower() != "uci_analysemode" for name in new_options): - engine._setoption("UCI_AnalyseMode", False) - if "Ponder" in engine.options: - engine._setoption("Ponder", ponder) - if "MultiPV" in engine.options: - engine._setoption("MultiPV", engine.options["MultiPV"].default) + if "UCI_AnalyseMode" in self.engine.options and "UCI_AnalyseMode" not in self.engine.target_config and all(name.lower() != "uci_analysemode" for name in new_options): + self.engine._setoption("UCI_AnalyseMode", False) + if "Ponder" in self.engine.options: + self.engine._setoption("Ponder", ponder) + if "MultiPV" in self.engine.options: + self.engine._setoption("MultiPV", self.engine.options["MultiPV"].default) - new_opponent = new_options.get("UCI_Opponent") or engine.target_config.get("UCI_Opponent") - opponent_changed = new_opponent != engine.config.get("UCI_Opponent") - engine._configure(new_options) + new_opponent = new_options.get("UCI_Opponent") or self.engine.target_config.get("UCI_Opponent") + opponent_changed = new_opponent != self.engine.config.get("UCI_Opponent") + self.engine._configure(new_options) - if engine.first_game or engine.game != game or opponent_changed: - engine.game = game - engine._ucinewgame() + if self.engine.first_game or self.engine.game != game or opponent_changed: + self.engine.game = game + self.engine._ucinewgame() self.sent_isready = True - engine._isready() + self.engine._isready() else: - self._readyok(engine) + self._readyok() - def line_received(self, engine: UciProtocol, line: str) -> None: + @typing.override + def line_received(self, line: str) -> None: token, remaining = _next_token(line) if token == "info": - self._info(engine, remaining) + self._info(remaining) elif token == "bestmove": - self._bestmove(engine, remaining) + self._bestmove(remaining) elif line.strip() == "readyok" and self.sent_isready: - self._readyok(engine) + self._readyok() else: - LOGGER.warning("%s: Unexpected engine output: %r", engine, line) + LOGGER.warning("%s: Unexpected engine output: %r", self.engine, line) - def _readyok(self, engine: UciProtocol) -> None: + def _readyok(self) -> None: self.sent_isready = False engine._position(board) engine._go(limit, root_moves=root_moves) - def _info(self, engine: UciProtocol, arg: str) -> None: + def _info(self, arg: str) -> None: if not self.pondering: - self.info.update(_parse_uci_info(arg, engine.board, info)) + self.info.update(_parse_uci_info(arg, self.engine.board, info)) - def _bestmove(self, engine: UciProtocol, arg: str) -> None: + def _bestmove(self, arg: str) -> None: if self.pondering: self.pondering = None elif not self.result.cancelled(): - best = _parse_uci_bestmove(engine.board, arg) + best = _parse_uci_bestmove(self.engine.board, arg) self.result.set_result(PlayResult(best.move, best.ponder, self.info)) if ponder and best.move and best.ponder: self.pondering = board.copy() self.pondering.push(best.move) self.pondering.push(best.ponder) - engine._position(self.pondering) + self.engine._position(self.pondering) # Adjust clocks for pondering. time_used = time.perf_counter() - self.start_time @@ -1627,89 +1653,98 @@ def _bestmove(self, engine: UciProtocol, arg: str) -> None: if ponder_limit.remaining_moves: ponder_limit.remaining_moves -= 1 - engine._go(ponder_limit, ponder=True) + self.engine._go(ponder_limit, ponder=True) if not self.pondering: - self.end(engine) + self.end() - def end(self, engine: UciProtocol) -> None: + def end(self) -> None: engine.may_ponderhit = None self.set_finished() - def cancel(self, engine: UciProtocol) -> None: - if engine.may_ponderhit and self.pondering and engine.may_ponderhit.move_stack == self.pondering.move_stack and engine.may_ponderhit == self.pondering: - engine.ponderhit = True - self.end(engine) + @typing.override + def cancel(self) -> None: + if self.engine.may_ponderhit and self.pondering and self.engine.may_ponderhit.move_stack == self.pondering.move_stack and self.engine.may_ponderhit == self.pondering: + self.engine.ponderhit = True + self.end() else: - engine.send_line("stop") + self.engine.send_line("stop") - def engine_terminated(self, engine: UciProtocol, exc: Exception) -> None: + @typing.override + def engine_terminated(self, exc: Exception) -> None: # Allow terminating engine while pondering. if not self.result.done(): - super().engine_terminated(engine, exc) + super().engine_terminated(exc) return await self.communicate(UciPlayCommand) async def analysis(self, board: chess.Board, limit: Optional[Limit] = None, *, multipv: Optional[int] = None, game: object = None, info: Info = INFO_ALL, root_moves: Optional[Iterable[chess.Move]] = None, options: ConfigMapping = {}) -> AnalysisResult: - class UciAnalysisCommand(BaseCommand[UciProtocol, AnalysisResult]): - def start(self, engine: UciProtocol) -> None: - self.analysis = AnalysisResult(stop=lambda: self.cancel(engine)) + class UciAnalysisCommand(BaseCommand[AnalysisResult]): + def __init__(self, engine: UciProtocol): + super().__init__(engine) + self.engine = engine + + def start(self) -> None: + self.analysis = AnalysisResult(stop=lambda: self.cancel()) self.sent_isready = False - if "Ponder" in engine.options: - engine._setoption("Ponder", False) - if "UCI_AnalyseMode" in engine.options and "UCI_AnalyseMode" not in engine.target_config and all(name.lower() != "uci_analysemode" for name in options): - engine._setoption("UCI_AnalyseMode", True) - if "MultiPV" in engine.options or (multipv and multipv > 1): - engine._setoption("MultiPV", 1 if multipv is None else multipv) + if "Ponder" in self.engine.options: + self.engine._setoption("Ponder", False) + if "UCI_AnalyseMode" in self.engine.options and "UCI_AnalyseMode" not in self.engine.target_config and all(name.lower() != "uci_analysemode" for name in options): + self.engine._setoption("UCI_AnalyseMode", True) + if "MultiPV" in self.engine.options or (multipv and multipv > 1): + self.engine._setoption("MultiPV", 1 if multipv is None else multipv) - engine._configure(options) + self.engine._configure(options) - if engine.first_game or engine.game != game: - engine.game = game - engine._ucinewgame() + if self.engine.first_game or self.engine.game != game: + self.engine.game = game + self.engine._ucinewgame() self.sent_isready = True - engine._isready() + self.engine._isready() else: - self._readyok(engine) + self._readyok() - def line_received(self, engine: UciProtocol, line: str) -> None: + @typing.override + def line_received(self, line: str) -> None: token, remaining = _next_token(line) if token == "info": - self._info(engine, remaining) + self._info(remaining) elif token == "bestmove": - self._bestmove(engine, remaining) + self._bestmove(remaining) elif line.strip() == "readyok" and self.sent_isready: - self._readyok(engine) + self._readyok() else: - LOGGER.warning("%s: Unexpected engine output: %r", engine, line) + LOGGER.warning("%s: Unexpected engine output: %r", self.engine, line) - def _readyok(self, engine: UciProtocol) -> None: + def _readyok(self) -> None: self.sent_isready = False - engine._position(board) + self.engine._position(board) if limit: - engine._go(limit, root_moves=root_moves) + self.engine._go(limit, root_moves=root_moves) else: - engine._go(Limit(), root_moves=root_moves, infinite=True) + self.engine._go(Limit(), root_moves=root_moves, infinite=True) self.result.set_result(self.analysis) - def _info(self, engine: UciProtocol, arg: str) -> None: - self.analysis.post(_parse_uci_info(arg, engine.board, info)) + def _info(self, arg: str) -> None: + self.analysis.post(_parse_uci_info(arg, self.engine.board, info)) - def _bestmove(self, engine: UciProtocol, arg: str) -> None: + def _bestmove(self, arg: str) -> None: if not self.result.done(): raise EngineError("was not searching, but engine sent bestmove") - best = _parse_uci_bestmove(engine.board, arg) + best = _parse_uci_bestmove(self.engine.board, arg) self.set_finished() self.analysis.set_finished(best) - def cancel(self, engine: UciProtocol) -> None: - engine.send_line("stop") + @typing.override + def cancel(self) -> None: + self.engine.send_line("stop") - def engine_terminated(self, engine: UciProtocol, exc: Exception) -> None: - LOGGER.debug("%s: Closing analysis because engine has been terminated (error: %s)", engine, exc) + @typing.override + def engine_terminated(self, exc: Exception) -> None: + LOGGER.debug("%s: Closing analysis because engine has been terminated (error: %s)", self.engine, exc) self.analysis.set_exception(exc) return await self.communicate(UciAnalysisCommand) @@ -1938,89 +1973,96 @@ def __init__(self) -> None: self.first_game = True async def initialize(self) -> None: - class XBoardInitializeCommand(BaseCommand[XBoardProtocol, None]): - def check_initialized(self, engine: XBoardProtocol) -> None: - if engine.initialized: + class XBoardInitializeCommand(BaseCommand[None]): + def __init__(self, engine: XBoardProtocol): + super().__init__(engine) + self.engine = engine + + @typing.override + def check_initialized(self) -> None: + if self.engine.initialized: raise EngineError("engine already initialized") - def start(self, engine: XBoardProtocol) -> None: - engine.send_line("xboard") - engine.send_line("protover 2") - self.timeout_handle = engine.loop.call_later(2.0, lambda: self.timeout(engine)) + @typing.override + def start(self) -> None: + self.engine.send_line("xboard") + self.engine.send_line("protover 2") + self.timeout_handle = self.engine.loop.call_later(2.0, lambda: self.timeout()) - def timeout(self, engine: XBoardProtocol) -> None: - LOGGER.error("%s: Timeout during initialization", engine) - self.end(engine) + def timeout(self) -> None: + LOGGER.error("%s: Timeout during initialization", self.engine) + self.end() - def line_received(self, engine: XBoardProtocol, line: str) -> None: + @typing.override + def line_received(self, line: str) -> None: token, remaining = _next_token(line) if token.startswith("#"): pass elif token == "feature": - self._feature(engine, remaining) + self._feature(remaining) elif XBOARD_ERROR_REGEX.match(line): raise EngineError(line) - def _feature(self, engine: XBoardProtocol, arg: str) -> None: + def _feature(self, arg: str) -> None: for feature in shlex.split(arg): key, value = feature.split("=", 1) if key == "option": option = _parse_xboard_option(value) if option.name not in ["random", "computer", "cores", "memory"]: - engine.options[option.name] = option + self.engine.options[option.name] = option else: try: - engine.features[key] = int(value) + self.engine.features[key] = int(value) except ValueError: - engine.features[key] = value + self.engine.features[key] = value - if "done" in engine.features: + if "done" in self.engine.features: self.timeout_handle.cancel() - if engine.features.get("done"): - self.end(engine) + if self.engine.features.get("done"): + self.end() - def end(self, engine: XBoardProtocol) -> None: - if not engine.features.get("ping", 0): + def end(self) -> None: + if not self.engine.features.get("ping", 0): self.result.set_exception(EngineError("xboard engine did not declare required feature: ping")) self.set_finished() return - if not engine.features.get("setboard", 0): + if not self.engine.features.get("setboard", 0): self.result.set_exception(EngineError("xboard engine did not declare required feature: setboard")) self.set_finished() return - if not engine.features.get("reuse", 1): - LOGGER.warning("%s: Rejecting feature reuse=0", engine) - engine.send_line("rejected reuse") - if not engine.features.get("sigterm", 1): - LOGGER.warning("%s: Rejecting feature sigterm=0", engine) - engine.send_line("rejected sigterm") - if engine.features.get("san", 0): - LOGGER.warning("%s: Rejecting feature san=1", engine) - engine.send_line("rejected san") - - if "myname" in engine.features: - engine.id["name"] = str(engine.features["myname"]) - - if engine.features.get("memory", 0): - engine.options["memory"] = Option("memory", "spin", 16, 1, None, None) - engine.send_line("accepted memory") - if engine.features.get("smp", 0): - engine.options["cores"] = Option("cores", "spin", 1, 1, None, None) - engine.send_line("accepted smp") - if engine.features.get("egt"): - for egt in str(engine.features["egt"]).split(","): + if not self.engine.features.get("reuse", 1): + LOGGER.warning("%s: Rejecting feature reuse=0", self.engine) + self.engine.send_line("rejected reuse") + if not self.engine.features.get("sigterm", 1): + LOGGER.warning("%s: Rejecting feature sigterm=0", self.engine) + self.engine.send_line("rejected sigterm") + if self.engine.features.get("san", 0): + LOGGER.warning("%s: Rejecting feature san=1", self.engine) + self.engine.send_line("rejected san") + + if "myname" in self.engine.features: + self.engine.id["name"] = str(self.engine.features["myname"]) + + if self.engine.features.get("memory", 0): + self.engine.options["memory"] = Option("memory", "spin", 16, 1, None, None) + self.engine.send_line("accepted memory") + if self.engine.features.get("smp", 0): + self.engine.options["cores"] = Option("cores", "spin", 1, 1, None, None) + self.engine.send_line("accepted smp") + if self.engine.features.get("egt"): + for egt in str(self.engine.features["egt"]).split(","): name = f"egtpath {egt}" - engine.options[name] = Option(name, "path", None, None, None, None) - engine.send_line("accepted egt") + self.engine.options[name] = Option(name, "path", None, None, None, None) + self.engine.send_line("accepted egt") - for option in engine.options.values(): + for option in self.engine.options.values(): if option.default is not None: - engine.config[option.name] = option.default + self.engine.config[option.name] = option.default if option.default is not None and not option.is_managed(): - engine.target_config[option.name] = option.default + self.engine.target_config[option.name] = option.default - engine.initialized = True + self.engine.initialized = True self.result.set_result(None) self.set_finished() @@ -2105,18 +2147,24 @@ def _new(self, board: chess.Board, game: object, options: ConfigMapping, opponen self.board.push(move) async def ping(self) -> None: - class XBoardPingCommand(BaseCommand[XBoardProtocol, None]): - def start(self, engine: XBoardProtocol) -> None: + class XBoardPingCommand(BaseCommand[None]): + def __init__(self, engine: XBoardProtocol): + super().__init__(engine) + self.engine = engine + + @typing.override + def start(self) -> None: n = id(self) & 0xffff self.pong = f"pong {n}" - engine._ping(n) + self.engine._ping(n) - def line_received(self, engine: XBoardProtocol, line: str) -> None: + @typing.override + def line_received(self, line: str) -> None: if line == self.pong: self.result.set_result(None) self.set_finished() elif not line.startswith("#"): - LOGGER.warning("%s: Unexpected engine output: %r", engine, line) + LOGGER.warning("%s: Unexpected engine output: %r", self.engine, line) elif XBOARD_ERROR_REGEX.match(line): raise EngineError(line) @@ -2126,54 +2174,60 @@ async def play(self, board: chess.Board, limit: Limit, *, game: object = None, i if root_moves is not None: raise EngineError("play with root_moves, but xboard supports 'include' only in analysis mode") - class XBoardPlayCommand(BaseCommand[XBoardProtocol, PlayResult]): - def start(self, engine: XBoardProtocol) -> None: + class XBoardPlayCommand(BaseCommand[PlayResult]): + def __init__(self, engine: XBoardProtocol): + super().__init__(engine) + self.engine = engine + + @typing.override + def start(self) -> None: self.play_result = PlayResult(None, None) self.stopped = False self.pong_after_move: Optional[str] = None self.pong_after_ponder: Optional[str] = None # Set game, position and configure. - engine._new(board, game, options, opponent) + self.engine._new(board, game, options, opponent) # Limit or time control. clock = limit.white_clock if board.turn else limit.black_clock increment = limit.white_inc if board.turn else limit.black_inc - if limit.clock_id is None or limit.clock_id != engine.clock_id: - self._send_time_control(engine, clock, increment) - engine.clock_id = limit.clock_id + if limit.clock_id is None or limit.clock_id != self.engine.clock_id: + self._send_time_control(clock, increment) + self.engine.clock_id = limit.clock_id if limit.nodes is not None: if limit.time is not None or limit.white_clock is not None or limit.black_clock is not None or increment is not None: raise EngineError("xboard does not support mixing node limits with time limits") - if "nps" not in engine.features: + if "nps" not in self.engine.features: LOGGER.warning("%s: Engine did not explicitly declare support for node limits (feature nps=?)") - elif not engine.features["nps"]: + elif not self.engine.features["nps"]: raise EngineError("xboard engine does not support node limits (feature nps=0)") - engine.send_line("nps 1") - engine.send_line(f"st {max(1, int(limit.nodes))}") + self.engine.send_line("nps 1") + self.engine.send_line(f"st {max(1, int(limit.nodes))}") if limit.depth is not None: - engine.send_line(f"sd {max(1, int(limit.depth))}") + self.engine.send_line(f"sd {max(1, int(limit.depth))}") if limit.white_clock is not None: - engine.send_line("{} {}".format("time" if board.turn else "otim", max(1, round(limit.white_clock * 100)))) + self.engine.send_line("{} {}".format("time" if board.turn else "otim", max(1, round(limit.white_clock * 100)))) if limit.black_clock is not None: - engine.send_line("{} {}".format("otim" if board.turn else "time", max(1, round(limit.black_clock * 100)))) + self.engine.send_line("{} {}".format("otim" if board.turn else "time", max(1, round(limit.black_clock * 100)))) - if draw_offered and engine.features.get("draw", 1): - engine.send_line("draw") + if draw_offered and self.engine.features.get("draw", 1): + self.engine.send_line("draw") # Start thinking. - engine.send_line("post" if info else "nopost") - engine.send_line("hard" if ponder else "easy") - engine.send_line("go") + self.engine.send_line("post" if info else "nopost") + self.engine.send_line("hard" if ponder else "easy") + self.engine.send_line("go") - def line_received(self, engine: XBoardProtocol, line: str) -> None: + @typing.override + def line_received(self, line: str) -> None: token, remaining = _next_token(line) if token == "move": - self._move(engine, remaining.strip()) + self._move(remaining.strip()) elif token == "Hint:": - self._hint(engine, remaining.strip()) + self._hint(remaining.strip()) elif token == "pong": pong_line = f"{token} {remaining.strip()}" if pong_line == self.pong_after_move: @@ -2188,84 +2242,86 @@ def line_received(self, engine: XBoardProtocol, line: str) -> None: elif f"{token} {remaining.strip()}" == "offer draw": if not self.result.done(): self.play_result.draw_offered = True - self._ping_after_move(engine) + self._ping_after_move() elif line.strip() == "resign": if not self.result.done(): self.play_result.resigned = True - self._ping_after_move(engine) + self._ping_after_move() elif token in ["1-0", "0-1", "1/2-1/2"]: if "resign" in line and not self.result.done(): self.play_result.resigned = True - self._ping_after_move(engine) + self._ping_after_move() elif token.startswith("#"): pass elif XBOARD_ERROR_REGEX.match(line): - engine.first_game = True # Board state might no longer be in sync + self.engine.first_game = True # Board state might no longer be in sync raise EngineError(line) elif len(line.split()) >= 4 and line.lstrip()[0].isdigit(): - self._post(engine, line) + self._post(line) else: - LOGGER.warning("%s: Unexpected engine output: %r", engine, line) + LOGGER.warning("%s: Unexpected engine output: %r", self.engine, line) - def _send_time_control(self, engine: XBoardProtocol, clock: Optional[float], increment: Optional[float]) -> None: + def _send_time_control(self, clock: Optional[float], increment: Optional[float]) -> None: if limit.remaining_moves or clock is not None or increment is not None: base_mins, base_secs = divmod(int(clock or 0), 60) - engine.send_line(f"level {limit.remaining_moves or 0} {base_mins}:{base_secs:02d} {increment or 0}") + self.engine.send_line(f"level {limit.remaining_moves or 0} {base_mins}:{base_secs:02d} {increment or 0}") if limit.time is not None: - engine.send_line(f"st {max(0.01, limit.time)}") + self.engine.send_line(f"st {max(0.01, limit.time)}") - def _post(self, engine: XBoardProtocol, line: str) -> None: + def _post(self, line: str) -> None: if not self.result.done(): - self.play_result.info = _parse_xboard_post(line, engine.board, info) + self.play_result.info = _parse_xboard_post(line, self.engine.board, info) - def _move(self, engine: XBoardProtocol, arg: str) -> None: + def _move(self, arg: str) -> None: if not self.result.done() and self.play_result.move is None: try: - self.play_result.move = engine.board.push_xboard(arg) + self.play_result.move = self.engine.board.push_xboard(arg) except ValueError as err: self.result.set_exception(EngineError(err)) else: - self._ping_after_move(engine) + self._ping_after_move() else: try: - engine.board.push_xboard(arg) + self.engine.board.push_xboard(arg) except ValueError: LOGGER.exception("Exception playing unexpected move") - def _hint(self, engine: XBoardProtocol, arg: str) -> None: + def _hint(self, arg: str) -> None: if not self.result.done() and self.play_result.move is not None and self.play_result.ponder is None: try: - self.play_result.ponder = engine.board.parse_xboard(arg) + self.play_result.ponder = self.engine.board.parse_xboard(arg) except ValueError: LOGGER.exception("Exception parsing hint") else: LOGGER.warning("Unexpected hint: %r", arg) - def _ping_after_move(self, engine: XBoardProtocol) -> None: + def _ping_after_move(self) -> None: if self.pong_after_move is None: n = id(self) & 0xffff self.pong_after_move = f"pong {n}" - engine._ping(n) + self.engine._ping(n) - def cancel(self, engine: XBoardProtocol) -> None: + @typing.override + def cancel(self) -> None: if self.stopped: return self.stopped = True if self.result.cancelled(): - engine.send_line("?") + self.engine.send_line("?") if ponder: - engine.send_line("easy") + self.engine.send_line("easy") n = (id(self) + 1) & 0xffff self.pong_after_ponder = f"pong {n}" - engine._ping(n) + self.engine._ping(n) - def engine_terminated(self, engine: XBoardProtocol, exc: Exception) -> None: + @typing.override + def engine_terminated(self, exc: Exception) -> None: # Allow terminating engine while pondering. if not self.result.done(): - super().engine_terminated(engine, exc) + super().engine_terminated(exc) return await self.communicate(XBoardPlayCommand) @@ -2276,49 +2332,55 @@ async def analysis(self, board: chess.Board, limit: Optional[Limit] = None, *, m if limit is not None and (limit.white_clock is not None or limit.black_clock is not None): raise EngineError("xboard analysis does not support clock limits") - class XBoardAnalysisCommand(BaseCommand[XBoardProtocol, AnalysisResult]): - def start(self, engine: XBoardProtocol) -> None: + class XBoardAnalysisCommand(BaseCommand[AnalysisResult]): + def __init__(self, engine: XBoardProtocol): + super().__init__(engine) + self.engine = engine + + @typing.override + def start(self) -> None: self.stopped = False self.best_move: Optional[chess.Move] = None - self.analysis = AnalysisResult(stop=lambda: self.cancel(engine)) + self.analysis = AnalysisResult(stop=lambda: self.cancel()) self.final_pong: Optional[str] = None - engine._new(board, game, options) + self.engine._new(board, game, options) if root_moves is not None: - if not engine.features.get("exclude", 0): + if not self.engine.features.get("exclude", 0): raise EngineError("xboard engine does not support root_moves (feature exclude=0)") - engine.send_line("exclude all") + self.engine.send_line("exclude all") for move in root_moves: - engine.send_line(f"include {engine.board.xboard(move)}") + self.engine.send_line(f"include {self.engine.board.xboard(move)}") - engine.send_line("post") - engine.send_line("analyze") + self.engine.send_line("post") + self.engine.send_line("analyze") self.result.set_result(self.analysis) if limit is not None and limit.time is not None: - self.time_limit_handle: Optional[asyncio.Handle] = engine.loop.call_later(limit.time, lambda: self.cancel(engine)) + self.time_limit_handle: Optional[asyncio.Handle] = self.engine.loop.call_later(limit.time, lambda: self.cancel()) else: self.time_limit_handle = None - def line_received(self, engine: XBoardProtocol, line: str) -> None: + @typing.override + def line_received(self, line: str) -> None: token, remaining = _next_token(line) if token.startswith("#"): pass elif len(line.split()) >= 4 and line.lstrip()[0].isdigit(): - self._post(engine, line) + self._post(line) elif f"{token} {remaining.strip()}" == self.final_pong: - self.end(engine) + self.end() elif XBOARD_ERROR_REGEX.match(line): - engine.first_game = True # Board state might no longer be in sync + self.engine.first_game = True # Board state might no longer be in sync raise EngineError(line) else: - LOGGER.warning("%s: Unexpected engine output: %r", engine, line) + LOGGER.warning("%s: Unexpected engine output: %r", self.engine, line) - def _post(self, engine: XBoardProtocol, line: str) -> None: - post_info = _parse_xboard_post(line, engine.board, info) + def _post(self, line: str) -> None: + post_info = _parse_xboard_post(line, self.engine.board, info) self.analysis.post(post_info) pv = post_info.get("pv") @@ -2327,36 +2389,38 @@ def _post(self, engine: XBoardProtocol, line: str) -> None: if limit is not None: if limit.time is not None and post_info.get("time", 0) >= limit.time: - self.cancel(engine) + self.cancel() elif limit.nodes is not None and post_info.get("nodes", 0) >= limit.nodes: - self.cancel(engine) + self.cancel() elif limit.depth is not None and post_info.get("depth", 0) >= limit.depth: - self.cancel(engine) + self.cancel() elif limit.mate is not None and "score" in post_info: if post_info["score"].relative >= Mate(limit.mate): - self.cancel(engine) + self.cancel() - def end(self, engine: XBoardProtocol) -> None: + def end(self) -> None: if self.time_limit_handle: self.time_limit_handle.cancel() self.set_finished() self.analysis.set_finished(BestMove(self.best_move, None)) - def cancel(self, engine: XBoardProtocol) -> None: + @typing.override + def cancel(self) -> None: if self.stopped: return self.stopped = True - engine.send_line(".") - engine.send_line("exit") + self.engine.send_line(".") + self.engine.send_line("exit") n = id(self) & 0xffff self.final_pong = f"pong {n}" - engine._ping(n) + self.engine._ping(n) - def engine_terminated(self, engine: XBoardProtocol, exc: Exception) -> None: - LOGGER.debug("%s: Closing analysis because engine has been terminated (error: %s)", engine, exc) + @typing.override + def engine_terminated(self, exc: Exception) -> None: + LOGGER.debug("%s: Closing analysis because engine has been terminated (error: %s)", self.engine, exc) if self.time_limit_handle: self.time_limit_handle.cancel() @@ -2397,10 +2461,15 @@ def _configure(self, options: ConfigMapping) -> None: self._setoption(name, value) async def configure(self, options: ConfigMapping) -> None: - class XBoardConfigureCommand(BaseCommand[XBoardProtocol, None]): - def start(self, engine: XBoardProtocol) -> None: - engine._configure(options) - engine.target_config.update({name: value for name, value in options.items() if value is not None}) + class XBoardConfigureCommand(BaseCommand[None]): + def __init__(self, engine: XBoardProtocol): + super().__init__(engine) + self.engine = engine + + @typing.override + def start(self) -> None: + self.engine._configure(options) + self.engine.target_config.update({name: value for name, value in options.items() if value is not None}) self.result.set_result(None) self.set_finished() @@ -2423,12 +2492,17 @@ async def send_opponent_information(self, *, opponent: Optional[Opponent] = None return await self.configure(self._opponent_configuration(opponent=opponent, engine_rating=engine_rating)) async def send_game_result(self, board: chess.Board, winner: Optional[Color] = None, game_ending: Optional[str] = None, game_complete: bool = True) -> None: - class XBoardGameResultCommand(BaseCommand[XBoardProtocol, None]): - def start(self, engine: XBoardProtocol) -> None: + class XBoardGameResultCommand(BaseCommand[None]): + def __init__(self, engine: XBoardProtocol): + super().__init__(engine) + self.engine = engine + + @typing.override + def start(self) -> None: if game_ending and any(c in game_ending for c in "{}\n\r"): raise EngineError(f"invalid line break or curly braces in game ending message: {game_ending!r}") - engine._new(board, engine.game, {}) # Send final moves to engine. + self.engine._new(board, self.engine.game, {}) # Send final moves to engine. outcome = board.outcome(claim_draw=True) @@ -2451,7 +2525,7 @@ def start(self, engine: XBoardProtocol) -> None: ending = "" ending_text = f"{{{ending}}}" if ending else "" - engine.send_line(f"result {result} {ending_text}".strip()) + self.engine.send_line(f"result {result} {ending_text}".strip()) self.result.set_result(None) self.set_finished() @@ -2845,7 +2919,7 @@ def id(self) -> Mapping[str, str]: future = asyncio.run_coroutine_threadsafe(coro, self.protocol.loop) return future.result() - def communicate(self, command_factory: Callable[[Protocol], BaseCommand[Protocol, T]]) -> T: + def communicate(self, command_factory: Callable[[Protocol], BaseCommand[T]]) -> T: with self._not_shut_down(): coro = self.protocol.communicate(command_factory) future = asyncio.run_coroutine_threadsafe(coro, self.protocol.loop) From 5d8e82da82156f5eae2d15f07e2c24d800c87a41 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 31 Jul 2024 16:05:29 +0200 Subject: [PATCH 047/116] Let variant boards manage their own stack --- chess/__init__.py | 9 +++---- chess/variant.py | 66 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/chess/__init__.py b/chess/__init__.py index a90df9ef6..b82f8559f 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -1542,7 +1542,7 @@ def from_chess960_pos(cls: Type[BaseBoardT], scharnagl: int) -> BaseBoardT: BoardT = TypeVar("BoardT", bound="Board") -class _BoardState(Generic[BoardT]): +class _BoardState: def __init__(self, board: BoardT) -> None: self.pawns = board.pawns @@ -1701,7 +1701,7 @@ def __init__(self: BoardT, fen: Optional[str] = STARTING_FEN, *, chess960: bool self.ep_square = None self.move_stack = [] - self._stack: List[_BoardState[BoardT]] = [] + self._stack: List[_BoardState] = [] if fen is None: self.clear() @@ -2304,9 +2304,6 @@ def is_repetition(self, count: int = 3) -> bool: return False - def _board_state(self: BoardT) -> _BoardState[BoardT]: - return _BoardState(self) - def _push_capture(self, move: Move, capture_square: Square, piece_type: PieceType, was_promoted: bool) -> None: pass @@ -2335,7 +2332,7 @@ def push(self: BoardT, move: Move) -> None: """ # Push move and remember board state. move = self._to_chess960(move) - board_state = self._board_state() + board_state = _BoardState(self) self.castling_rights = self.clean_castling_rights() # Before pushing stack self.move_stack.append(self._from_chess960(self.chess960, move.from_square, move.to_square, move.promotion, move.drop)) self._stack.append(board_state) diff --git a/chess/variant.py b/chess/variant.py index 6160696a2..119a0402c 100644 --- a/chess/variant.py +++ b/chess/variant.py @@ -673,14 +673,12 @@ def status(self) -> chess.Status: ThreeCheckBoardT = TypeVar("ThreeCheckBoardT", bound="ThreeCheckBoard") -class _ThreeCheckBoardState(Generic[ThreeCheckBoardT], chess._BoardState[ThreeCheckBoardT]): - def __init__(self, board: ThreeCheckBoardT) -> None: - super().__init__(board) +class _ThreeCheckBoardState: + def __init__(self, board: ThreeCheckBoard) -> None: self.remaining_checks_w = board.remaining_checks[chess.WHITE] self.remaining_checks_b = board.remaining_checks[chess.BLACK] - def restore(self, board: ThreeCheckBoardT) -> None: - super().restore(board) + def restore(self, board: ThreeCheckBoard) -> None: board.remaining_checks[chess.WHITE] = self.remaining_checks_w board.remaining_checks[chess.BLACK] = self.remaining_checks_b @@ -698,8 +696,13 @@ class ThreeCheckBoard(chess.Board): def __init__(self, fen: Optional[str] = starting_fen, chess960: bool = False) -> None: self.remaining_checks = [3, 3] + self._three_check_stack: List[_ThreeCheckBoardState] = [] super().__init__(fen, chess960=chess960) + def clear_stack(self) -> None: + super().clear_stack() + self._three_check_stack.clear() + def reset_board(self) -> None: super().reset_board() self.remaining_checks[chess.WHITE] = 3 @@ -710,14 +713,17 @@ def clear_board(self) -> None: self.remaining_checks[chess.WHITE] = 3 self.remaining_checks[chess.BLACK] = 3 - def _board_state(self: ThreeCheckBoardT) -> _ThreeCheckBoardState[ThreeCheckBoardT]: - return _ThreeCheckBoardState(self) - def push(self, move: chess.Move) -> None: + self._three_check_stack.append(_ThreeCheckBoardState(self)) super().push(move) if self.is_check(): self.remaining_checks[not self.turn] -= 1 + def pop(self) -> chess.Move: + move = super().pop() + self._three_check_stack.pop().restore(self) + return move + def has_insufficient_material(self, color: chess.Color) -> bool: # Any remaining piece can give check. return not (self.occupied_co[color] & ~self.kings) @@ -792,8 +798,19 @@ def _transposition_key(self) -> Hashable: def copy(self: ThreeCheckBoardT, stack: Union[bool, int] = True) -> ThreeCheckBoardT: board = super().copy(stack=stack) board.remaining_checks = self.remaining_checks.copy() + if stack: + stack = len(self.move_stack) if stack is True else stack + board._three_check_stack = self._three_check_stack[-stack:] return board + def root(self: ThreeCheckBoardT) -> ThreeCheckBoardT: + if self._three_check_stack: + board = super().root() + self._three_check_stack[0].restore(board) + return board + else: + return self.copy(stack=False) + def mirror(self: ThreeCheckBoardT) -> ThreeCheckBoardT: board = super().mirror() board.remaining_checks[chess.WHITE] = self.remaining_checks[chess.BLACK] @@ -803,14 +820,12 @@ def mirror(self: ThreeCheckBoardT) -> ThreeCheckBoardT: CrazyhouseBoardT = TypeVar("CrazyhouseBoardT", bound="CrazyhouseBoard") -class _CrazyhouseBoardState(Generic[CrazyhouseBoardT], chess._BoardState[CrazyhouseBoardT]): - def __init__(self, board: CrazyhouseBoardT) -> None: - super().__init__(board) +class _CrazyhouseBoardState: + def __init__(self, board: CrazyhouseBoard) -> None: self.pockets_w = board.pockets[chess.WHITE].copy() self.pockets_b = board.pockets[chess.BLACK].copy() - def restore(self, board: CrazyhouseBoardT) -> None: - super().restore(board) + def restore(self, board: CrazyhouseBoard) -> None: board.pockets[chess.WHITE] = self.pockets_w board.pockets[chess.BLACK] = self.pockets_b @@ -870,8 +885,13 @@ class CrazyhouseBoard(chess.Board): def __init__(self, fen: Optional[str] = starting_fen, chess960: bool = False) -> None: self.pockets = [CrazyhousePocket(), CrazyhousePocket()] + self._crazyhouse_stack: List[_CrazyhouseBoardState] = [] super().__init__(fen, chess960=chess960) + def clear_stack(self) -> None: + super().clear_stack() + self._crazyhouse_stack.clear() + def reset_board(self) -> None: super().reset_board() self.pockets[chess.WHITE].reset() @@ -882,10 +902,8 @@ def clear_board(self) -> None: self.pockets[chess.WHITE].reset() self.pockets[chess.BLACK].reset() - def _board_state(self: CrazyhouseBoardT) -> _CrazyhouseBoardState[CrazyhouseBoardT]: - return _CrazyhouseBoardState(self) - def push(self, move: chess.Move) -> None: + self._crazyhouse_stack.append(_CrazyhouseBoardState(self)) super().push(move) if move.drop: self.pockets[not self.turn].remove(move.drop) @@ -896,6 +914,11 @@ def _push_capture(self, move: chess.Move, capture_square: chess.Square, piece_ty else: self.pockets[self.turn].add(piece_type) + def pop(self) -> chess.Move: + move = super().pop() + self._crazyhouse_stack.pop().restore(self) + return move + def _is_halfmoves(self, n: int) -> bool: # No draw by 50-move rule or 75-move rule. return False @@ -1028,8 +1051,19 @@ def copy(self: CrazyhouseBoardT, stack: Union[bool, int] = True) -> CrazyhouseBo board = super().copy(stack=stack) board.pockets[chess.WHITE] = self.pockets[chess.WHITE].copy() board.pockets[chess.BLACK] = self.pockets[chess.BLACK].copy() + if stack: + stack = len(self.move_stack) if stack is True else stack + board._crazyhouse_stack = self._crazyhouse_stack[-stack:] return board + def root(self: CrazyhouseBoardT) -> CrazyhouseBoardT: + if self._crazyhouse_stack: + board = super().root() + self._crazyhouse_stack[0].restore(board) + return board + else: + return self.copy(stack=False) + def mirror(self: CrazyhouseBoardT) -> CrazyhouseBoardT: board = super().mirror() board.pockets[chess.WHITE] = self.pockets[chess.BLACK].copy() From a652aaad5d22edd4cc078dba9214fb1d81789d7a Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 31 Jul 2024 16:13:51 +0200 Subject: [PATCH 048/116] Add fallback for override decorator --- chess/engine.py | 60 ++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/chess/engine.py b/chess/engine.py index d1bbaaee1..f5fdff96a 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -24,6 +24,14 @@ from types import TracebackType from typing import Any, Callable, Coroutine, Deque, Dict, Generator, Generic, Iterable, Iterator, List, Literal, Mapping, MutableMapping, Optional, Tuple, Type, TypedDict, TypeVar, Union +try: + from typing import override +except: + # Before Python 3.12 + F = typing.TypeVar("F", bound=Callable[..., Any]) + def override(fn: F, /) -> F: + return fn + if typing.TYPE_CHECKING: from typing_extensions import Self @@ -1317,16 +1325,16 @@ def __init__(self, engine: UciProtocol): super().__init__(engine) self.engine = engine - @typing.override + @override def check_initialized(self) -> None: if self.engine.initialized: raise EngineError("engine already initialized") - @typing.override + @override def start(self) -> None: self.engine.send_line("uci") - @typing.override + @override def line_received(self, line: str) -> None: token, remaining = _next_token(line) if line.strip() == "uciok" and not self.result.done(): @@ -1415,7 +1423,7 @@ def __init__(self, engine: UciProtocol) -> None: def start(self) -> None: self.engine._isready() - @typing.override + @override def line_received(self, line: str) -> None: if line.strip() == "readyok": self.result.set_result(None) @@ -1574,7 +1582,7 @@ def __init__(self, engine: UciProtocol): # MultiPV never change between pondering play commands. engine.may_ponderhit = board if ponder and not engine.first_game and game == engine.game and not engine._changed_options(new_options) else None - @typing.override + @override def start(self) -> None: self.info: InfoDict = {} self.pondering: Optional[chess.Board] = None @@ -1605,7 +1613,7 @@ def start(self) -> None: else: self._readyok() - @typing.override + @override def line_received(self, line: str) -> None: token, remaining = _next_token(line) if token == "info": @@ -1662,7 +1670,7 @@ def end(self) -> None: engine.may_ponderhit = None self.set_finished() - @typing.override + @override def cancel(self) -> None: if self.engine.may_ponderhit and self.pondering and self.engine.may_ponderhit.move_stack == self.pondering.move_stack and self.engine.may_ponderhit == self.pondering: self.engine.ponderhit = True @@ -1670,7 +1678,7 @@ def cancel(self) -> None: else: self.engine.send_line("stop") - @typing.override + @override def engine_terminated(self, exc: Exception) -> None: # Allow terminating engine while pondering. if not self.result.done(): @@ -1705,7 +1713,7 @@ def start(self) -> None: else: self._readyok() - @typing.override + @override def line_received(self, line: str) -> None: token, remaining = _next_token(line) if token == "info": @@ -1738,11 +1746,11 @@ def _bestmove(self, arg: str) -> None: self.set_finished() self.analysis.set_finished(best) - @typing.override + @override def cancel(self) -> None: self.engine.send_line("stop") - @typing.override + @override def engine_terminated(self, exc: Exception) -> None: LOGGER.debug("%s: Closing analysis because engine has been terminated (error: %s)", self.engine, exc) self.analysis.set_exception(exc) @@ -1978,12 +1986,12 @@ def __init__(self, engine: XBoardProtocol): super().__init__(engine) self.engine = engine - @typing.override + @override def check_initialized(self) -> None: if self.engine.initialized: raise EngineError("engine already initialized") - @typing.override + @override def start(self) -> None: self.engine.send_line("xboard") self.engine.send_line("protover 2") @@ -1993,7 +2001,7 @@ def timeout(self) -> None: LOGGER.error("%s: Timeout during initialization", self.engine) self.end() - @typing.override + @override def line_received(self, line: str) -> None: token, remaining = _next_token(line) if token.startswith("#"): @@ -2152,13 +2160,13 @@ def __init__(self, engine: XBoardProtocol): super().__init__(engine) self.engine = engine - @typing.override + @override def start(self) -> None: n = id(self) & 0xffff self.pong = f"pong {n}" self.engine._ping(n) - @typing.override + @override def line_received(self, line: str) -> None: if line == self.pong: self.result.set_result(None) @@ -2179,7 +2187,7 @@ def __init__(self, engine: XBoardProtocol): super().__init__(engine) self.engine = engine - @typing.override + @override def start(self) -> None: self.play_result = PlayResult(None, None) self.stopped = False @@ -2221,7 +2229,7 @@ def start(self) -> None: self.engine.send_line("hard" if ponder else "easy") self.engine.send_line("go") - @typing.override + @override def line_received(self, line: str) -> None: token, remaining = _next_token(line) if token == "move": @@ -2301,7 +2309,7 @@ def _ping_after_move(self) -> None: self.pong_after_move = f"pong {n}" self.engine._ping(n) - @typing.override + @override def cancel(self) -> None: if self.stopped: return @@ -2317,7 +2325,7 @@ def cancel(self) -> None: self.pong_after_ponder = f"pong {n}" self.engine._ping(n) - @typing.override + @override def engine_terminated(self, exc: Exception) -> None: # Allow terminating engine while pondering. if not self.result.done(): @@ -2337,7 +2345,7 @@ def __init__(self, engine: XBoardProtocol): super().__init__(engine) self.engine = engine - @typing.override + @override def start(self) -> None: self.stopped = False self.best_move: Optional[chess.Move] = None @@ -2364,7 +2372,7 @@ def start(self) -> None: else: self.time_limit_handle = None - @typing.override + @override def line_received(self, line: str) -> None: token, remaining = _next_token(line) if token.startswith("#"): @@ -2405,7 +2413,7 @@ def end(self) -> None: self.set_finished() self.analysis.set_finished(BestMove(self.best_move, None)) - @typing.override + @override def cancel(self) -> None: if self.stopped: return @@ -2418,7 +2426,7 @@ def cancel(self) -> None: self.final_pong = f"pong {n}" self.engine._ping(n) - @typing.override + @override def engine_terminated(self, exc: Exception) -> None: LOGGER.debug("%s: Closing analysis because engine has been terminated (error: %s)", self.engine, exc) @@ -2466,7 +2474,7 @@ def __init__(self, engine: XBoardProtocol): super().__init__(engine) self.engine = engine - @typing.override + @override def start(self) -> None: self.engine._configure(options) self.engine.target_config.update({name: value for name, value in options.items() if value is not None}) @@ -2497,7 +2505,7 @@ def __init__(self, engine: XBoardProtocol): super().__init__(engine) self.engine = engine - @typing.override + @override def start(self) -> None: if game_ending and any(c in game_ending for c in "{}\n\r"): raise EngineError(f"invalid line break or curly braces in game ending message: {game_ending!r}") From 7a59a3683f33682c0c2c9b359469c8b9876ece9e Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 31 Jul 2024 17:02:12 +0200 Subject: [PATCH 049/116] Import override from typing_extensions --- chess/engine.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/chess/engine.py b/chess/engine.py index f5fdff96a..347acf297 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -24,10 +24,9 @@ from types import TracebackType from typing import Any, Callable, Coroutine, Deque, Dict, Generator, Generic, Iterable, Iterator, List, Literal, Mapping, MutableMapping, Optional, Tuple, Type, TypedDict, TypeVar, Union -try: - from typing import override -except: - # Before Python 3.12 +if typing.TYPE_CHECKING: + from typing_extensions import override +else: F = typing.TypeVar("F", bound=Callable[..., Any]) def override(fn: F, /) -> F: return fn From 571658baa358b97f3cb5104c3dfab33c6d962deb Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 31 Jul 2024 17:25:03 +0200 Subject: [PATCH 050/116] Use typing_extensions.Self --- chess/__init__.py | 32 ++++++++++++++++---------------- chess/engine.py | 2 +- chess/pgn.py | 7 +++++-- chess/syzygy.py | 7 ++++--- chess/variant.py | 18 +++++++++++------- 5 files changed, 37 insertions(+), 29 deletions(-) diff --git a/chess/__init__.py b/chess/__init__.py index b82f8559f..3553eee65 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -25,7 +25,7 @@ from typing import ClassVar, Callable, Counter, Dict, Generic, Hashable, Iterable, Iterator, List, Literal, Mapping, Optional, SupportsInt, Tuple, Type, TypeVar, Union if typing.TYPE_CHECKING: - from typing_extensions import TypeAlias + from typing_extensions import Self, TypeAlias EnPassantSpec = Literal["legal", "fen", "xfen"] @@ -1455,7 +1455,7 @@ def apply_transform(self, f: Callable[[Bitboard], Bitboard]) -> None: self.occupied = f(self.occupied) self.promoted = f(self.promoted) - def transform(self: BaseBoardT, f: Callable[[Bitboard], Bitboard]) -> BaseBoardT: + def transform(self, f: Callable[[Bitboard], Bitboard]) -> Self: """ Returns a transformed copy of the board (without move stack) by applying a bitboard transformation function. @@ -1473,11 +1473,11 @@ def transform(self: BaseBoardT, f: Callable[[Bitboard], Bitboard]) -> BaseBoardT board.apply_transform(f) return board - def apply_mirror(self: BaseBoardT) -> None: + def apply_mirror(self) -> None: self.apply_transform(flip_vertical) self.occupied_co[WHITE], self.occupied_co[BLACK] = self.occupied_co[BLACK], self.occupied_co[WHITE] - def mirror(self: BaseBoardT) -> BaseBoardT: + def mirror(self) -> Self: """ Returns a mirrored copy of the board (without move stack). @@ -1491,7 +1491,7 @@ def mirror(self: BaseBoardT) -> BaseBoardT: board.apply_mirror() return board - def copy(self: BaseBoardT) -> BaseBoardT: + def copy(self) -> Self: """Creates a copy of the board.""" board = type(self)(None) @@ -1509,10 +1509,10 @@ def copy(self: BaseBoardT) -> BaseBoardT: return board - def __copy__(self: BaseBoardT) -> BaseBoardT: + def __copy__(self) -> Self: return self.copy() - def __deepcopy__(self: BaseBoardT, memo: Dict[int, object]) -> BaseBoardT: + def __deepcopy__(self, memo: Dict[int, object]) -> Self: board = self.copy() memo[id(self)] = board return board @@ -1694,7 +1694,7 @@ class Board(BaseBoard): manipulation. """ - def __init__(self: BoardT, fen: Optional[str] = STARTING_FEN, *, chess960: bool = False) -> None: + def __init__(self, fen: Optional[str] = STARTING_FEN, *, chess960: bool = False) -> None: BaseBoard.__init__(self, None) self.chess960 = chess960 @@ -1786,7 +1786,7 @@ def clear_stack(self) -> None: self.move_stack.clear() self._stack.clear() - def root(self: BoardT) -> BoardT: + def root(self) -> Self: """Returns a copy of the root position.""" if self._stack: board = type(self)(None, chess960=self.chess960) @@ -2307,7 +2307,7 @@ def is_repetition(self, count: int = 3) -> bool: def _push_capture(self, move: Move, capture_square: Square, piece_type: PieceType, was_promoted: bool) -> None: pass - def push(self: BoardT, move: Move) -> None: + def push(self, move: Move) -> None: """ Updates the position with the given *move* and puts it onto the move stack. @@ -2428,7 +2428,7 @@ def push(self: BoardT, move: Move) -> None: # Swap turn. self.turn = not self.turn - def pop(self: BoardT) -> Move: + def pop(self) -> Move: """ Restores the previous position and returns the last move from the stack. @@ -2838,7 +2838,7 @@ def _validate_epd_opcode(self, opcode: str) -> None: if blacklisted in opcode: raise ValueError(f"invalid character {blacklisted!r} in epd opcode: {opcode!r}") - def _parse_epd_ops(self: BoardT, operation_part: str, make_board: Callable[[], BoardT]) -> Dict[str, Union[None, str, int, float, Move, List[Move]]]: + def _parse_epd_ops(self, operation_part: str, make_board: Callable[[], Self]) -> Dict[str, Union[None, str, int, float, Move, List[Move]]]: operations: Dict[str, Union[None, str, int, float, Move, List[Move]]] = {} state = "opcode" opcode = "" @@ -3831,16 +3831,16 @@ def apply_transform(self, f: Callable[[Bitboard], Bitboard]) -> None: self.ep_square = None if self.ep_square is None else msb(f(BB_SQUARES[self.ep_square])) self.castling_rights = f(self.castling_rights) - def transform(self: BoardT, f: Callable[[Bitboard], Bitboard]) -> BoardT: + def transform(self, f: Callable[[Bitboard], Bitboard]) -> Self: board = self.copy(stack=False) board.apply_transform(f) return board - def apply_mirror(self: BoardT) -> None: + def apply_mirror(self) -> None: super().apply_mirror() self.turn = not self.turn - def mirror(self: BoardT) -> BoardT: + def mirror(self) -> Self: """ Returns a mirrored copy of the board. @@ -3855,7 +3855,7 @@ def mirror(self: BoardT) -> BoardT: board.apply_mirror() return board - def copy(self: BoardT, *, stack: Union[bool, int] = True) -> BoardT: + def copy(self, *, stack: Union[bool, int] = True) -> Self: """ Creates a copy of the board. diff --git a/chess/engine.py b/chess/engine.py index 347acf297..59b843b8b 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -970,7 +970,7 @@ def pipe_data_received(self, fd: int, data: Union[bytes, str]) -> None: def error_line_received(self, line: str) -> None: LOGGER.warning("%s: stderr >> %s", self, line) - def _line_received(self: Protocol, line: str) -> None: + def _line_received(self, line: str) -> None: LOGGER.debug("%s: >> %s", self, line) self.line_received(line) diff --git a/chess/pgn.py b/chess/pgn.py index 0d0879ae0..83ddaf8f5 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -15,6 +15,9 @@ from typing import Any, Callable, Dict, Generic, Iterable, Iterator, List, Literal, Mapping, MutableMapping, Set, TextIO, Tuple, Type, TypeVar, Optional, Union from chess import Color, Square +if typing.TYPE_CHECKING: + from typing_extensions import Self + LOGGER = logging.getLogger(__name__) @@ -1014,10 +1017,10 @@ def __iter__(self) -> Iterator[str]: def __len__(self) -> int: return len(self._tag_roster) + len(self._others) - def copy(self: HeadersT) -> HeadersT: + def copy(self) -> Self: return type(self)(self) - def __copy__(self: HeadersT) -> HeadersT: + def __copy__(self) -> Self: return self.copy() def __repr__(self) -> str: diff --git a/chess/syzygy.py b/chess/syzygy.py index 8ba1cbca4..77a6eeded 100644 --- a/chess/syzygy.py +++ b/chess/syzygy.py @@ -14,6 +14,9 @@ from types import TracebackType from typing import Deque, Dict, Iterable, Iterator, List, Optional, Set, Tuple, Type, TypeVar, Union +if typing.TYPE_CHECKING: + from typing_extensions import Self + UINT64_BE = struct.Struct(">Q") UINT32 = struct.Struct(" None: self.data.close() self.data = None - def __enter__(self: TableT) -> TableT: + def __enter__(self) -> Self: return self def __exit__(self, exc_type: Optional[Type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[TracebackType]) -> None: diff --git a/chess/variant.py b/chess/variant.py index 119a0402c..d28a6e964 100644 --- a/chess/variant.py +++ b/chess/variant.py @@ -2,9 +2,13 @@ import chess import itertools +import typing from typing import Dict, Generic, Hashable, Iterable, Iterator, List, Optional, Type, TypeVar, Union +if typing.TYPE_CHECKING: + from typing_extensions import Self + class SuicideBoard(chess.Board): @@ -795,7 +799,7 @@ def _transposition_key(self) -> Hashable: return (super()._transposition_key(), self.remaining_checks[chess.WHITE], self.remaining_checks[chess.BLACK]) - def copy(self: ThreeCheckBoardT, stack: Union[bool, int] = True) -> ThreeCheckBoardT: + def copy(self, stack: Union[bool, int] = True) -> Self: board = super().copy(stack=stack) board.remaining_checks = self.remaining_checks.copy() if stack: @@ -803,7 +807,7 @@ def copy(self: ThreeCheckBoardT, stack: Union[bool, int] = True) -> ThreeCheckBo board._three_check_stack = self._three_check_stack[-stack:] return board - def root(self: ThreeCheckBoardT) -> ThreeCheckBoardT: + def root(self) -> Self: if self._three_check_stack: board = super().root() self._three_check_stack[0].restore(board) @@ -811,7 +815,7 @@ def root(self: ThreeCheckBoardT) -> ThreeCheckBoardT: else: return self.copy(stack=False) - def mirror(self: ThreeCheckBoardT) -> ThreeCheckBoardT: + def mirror(self) -> Self: board = super().mirror() board.remaining_checks[chess.WHITE] = self.remaining_checks[chess.BLACK] board.remaining_checks[chess.BLACK] = self.remaining_checks[chess.WHITE] @@ -865,7 +869,7 @@ def __len__(self) -> int: def __repr__(self) -> str: return f"CrazyhousePocket('{self}')" - def copy(self: CrazyhousePocketT) -> CrazyhousePocketT: + def copy(self) -> Self: """Returns a copy of this pocket.""" pocket = type(self)() pocket._pieces = self._pieces[:] @@ -1047,7 +1051,7 @@ def epd(self, shredder: bool = False, en_passant: chess.EnPassantSpec = "legal", board_part, info_part = epd.split(" ", 1) return f"{board_part}[{str(self.pockets[chess.WHITE]).upper()}{self.pockets[chess.BLACK]}] {info_part}" - def copy(self: CrazyhouseBoardT, stack: Union[bool, int] = True) -> CrazyhouseBoardT: + def copy(self, stack: Union[bool, int] = True) -> Self: board = super().copy(stack=stack) board.pockets[chess.WHITE] = self.pockets[chess.WHITE].copy() board.pockets[chess.BLACK] = self.pockets[chess.BLACK].copy() @@ -1056,7 +1060,7 @@ def copy(self: CrazyhouseBoardT, stack: Union[bool, int] = True) -> CrazyhouseBo board._crazyhouse_stack = self._crazyhouse_stack[-stack:] return board - def root(self: CrazyhouseBoardT) -> CrazyhouseBoardT: + def root(self) -> Self: if self._crazyhouse_stack: board = super().root() self._crazyhouse_stack[0].restore(board) @@ -1064,7 +1068,7 @@ def root(self: CrazyhouseBoardT) -> CrazyhouseBoardT: else: return self.copy(stack=False) - def mirror(self: CrazyhouseBoardT) -> CrazyhouseBoardT: + def mirror(self) -> Self: board = super().mirror() board.pockets[chess.WHITE] = self.pockets[chess.BLACK].copy() board.pockets[chess.BLACK] = self.pockets[chess.WHITE].copy() From f286d687e496123fb46d26ce3fd06bc0db505dc4 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 31 Jul 2024 17:40:10 +0200 Subject: [PATCH 051/116] Towards pyright compatibility --- chess/__init__.py | 4 ++-- chess/engine.py | 7 +++++-- chess/variant.py | 8 ++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/chess/__init__.py b/chess/__init__.py index 3553eee65..55bd4cad2 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -1544,7 +1544,7 @@ def from_chess960_pos(cls: Type[BaseBoardT], scharnagl: int) -> BaseBoardT: class _BoardState: - def __init__(self, board: BoardT) -> None: + def __init__(self, board: Board) -> None: self.pawns = board.pawns self.knights = board.knights self.bishops = board.bishops @@ -1564,7 +1564,7 @@ def __init__(self, board: BoardT) -> None: self.halfmove_clock = board.halfmove_clock self.fullmove_number = board.fullmove_number - def restore(self, board: BoardT) -> None: + def restore(self, board: Board) -> None: board.pawns = self.pawns board.knights = self.knights board.bishops = self.bishops diff --git a/chess/engine.py b/chess/engine.py index 59b843b8b..9a6a21f48 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -406,8 +406,10 @@ class Score(abc.ABC): """ @typing.overload + @abc.abstractmethod def score(self, *, mate_score: int) -> int: ... @typing.overload + @abc.abstractmethod def score(self, *, mate_score: Optional[int] = None) -> Optional[int]: ... @abc.abstractmethod def score(self, *, mate_score: Optional[int] = None) -> Optional[int]: @@ -2120,12 +2122,13 @@ def _new(self, board: chess.Board, game: object, options: ConfigMapping, opponen if self.config.get("computer"): self.send_line("computer") - self.send_line("force") + self.send_line("force") - if new_game: fen = root.fen(shredder=board.chess960, en_passant="fen") if variant != "normal" or fen != chess.STARTING_FEN or board.chess960: self.send_line(f"setboard {fen}") + else: + self.send_line("force") # Undo moves until common position. common_stack_len = 0 diff --git a/chess/variant.py b/chess/variant.py index d28a6e964..6e9161dc8 100644 --- a/chess/variant.py +++ b/chess/variant.py @@ -133,7 +133,7 @@ def _transposition_key(self) -> Hashable: else: return super()._transposition_key() - def board_fen(self, promoted: Optional[bool] = None) -> str: + def board_fen(self, *, promoted: Optional[bool] = None) -> str: if promoted is None: promoted = self.has_chess960_castling_rights() return super().board_fen(promoted=promoted) @@ -799,7 +799,7 @@ def _transposition_key(self) -> Hashable: return (super()._transposition_key(), self.remaining_checks[chess.WHITE], self.remaining_checks[chess.BLACK]) - def copy(self, stack: Union[bool, int] = True) -> Self: + def copy(self, *, stack: Union[bool, int] = True) -> Self: board = super().copy(stack=stack) board.remaining_checks = self.remaining_checks.copy() if stack: @@ -1041,7 +1041,7 @@ def set_fen(self, fen: str) -> None: self.pockets[chess.WHITE] = white_pocket self.pockets[chess.BLACK] = black_pocket - def board_fen(self, promoted: Optional[bool] = None) -> str: + def board_fen(self, *, promoted: Optional[bool] = None) -> str: if promoted is None: promoted = True return super().board_fen(promoted=promoted) @@ -1051,7 +1051,7 @@ def epd(self, shredder: bool = False, en_passant: chess.EnPassantSpec = "legal", board_part, info_part = epd.split(" ", 1) return f"{board_part}[{str(self.pockets[chess.WHITE]).upper()}{self.pockets[chess.BLACK]}] {info_part}" - def copy(self, stack: Union[bool, int] = True) -> Self: + def copy(self, *, stack: Union[bool, int] = True) -> Self: board = super().copy(stack=stack) board.pockets[chess.WHITE] = self.pockets[chess.WHITE].copy() board.pockets[chess.BLACK] = self.pockets[chess.BLACK].copy() From 33eea7ca062ed7396590ac3408ec05121bba9483 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 31 Jul 2024 18:01:47 +0200 Subject: [PATCH 052/116] Add changelog entries --- CHANGELOG.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a4534badb..754b5d418 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,13 @@ Changes: some 8 piece positions with decisive captures can be probed successfully. * The string wrapper returned by ``chess.svg`` functions now also implements ``_repr_html_``. +* Significant changes to ``chess.engine`` internals: + ``chess.engine.BaseCommand`` methods other than the constructor no longer + receive ``engine: Protocol``. +* Significant changes to board state internals: Subclasses of ``chess.Board`` + can no longer hook into board state recording/restoration and need to + override relevant methods instead (``clear_stack``, ``copy``, ``root``, + ``push``, ``pop``). New features: From caefd4dc6c25369750f6cc461885adfbbd52f09c Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 31 Jul 2024 21:11:38 +0200 Subject: [PATCH 053/116] chess.engine._next_token() cosmetics --- chess/engine.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/chess/engine.py b/chess/engine.py index 9a6a21f48..c6698bc76 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -2650,7 +2650,8 @@ def _parse_xboard_post(line: str, root_board: chess.Board, selector: Info = INFO def _next_token(line: str) -> tuple[str, str]: - """Get the next token in a whitespace-delimited line of text. + """ + Get the next token in a whitespace-delimited line of text. The result is returned as a 2-part tuple of strings. @@ -2660,10 +2661,10 @@ def _next_token(line: str) -> tuple[str, str]: If the input line is not empty and not completely whitespace, then the first element of the returned tuple is a single word with leading and trailing whitespace removed. The second element is the - unchanged rest of the line.""" - + unchanged rest of the line. + """ parts = line.split(maxsplit=1) - return (parts[0] if parts else "", parts[1] if len(parts) == 2 else "") + return parts[0] if parts else "", parts[1] if len(parts) == 2 else "" class BestMove: From 71e7c31fba31554a2b174ff7fb88a77b61674543 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 31 Jul 2024 22:21:28 +0200 Subject: [PATCH 054/116] Show actual state in engine command state assertions (#1049, #1071) --- chess/engine.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/chess/engine.py b/chess/engine.py index c6698bc76..302aa2bbd 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -989,7 +989,7 @@ async def communicate(self, command_factory: Callable[[Self], BaseCommand[T]]) - if self.returncode.done(): raise EngineTerminatedError(f"engine process dead (exit code: {self.returncode.result()})") - assert command.state == CommandState.NEW + assert command.state == CommandState.NEW, command.state if self.next_command is not None: self.next_command.result.cancel() @@ -1253,7 +1253,7 @@ def _handle_exception(self, exc: Exception) -> None: self.finished.set_result(None) def set_finished(self) -> None: - assert self.state in [CommandState.ACTIVE, CommandState.CANCELLING] + assert self.state in [CommandState.ACTIVE, CommandState.CANCELLING], self.state if not self.result.done(): self.result.set_exception(EngineError(f"engine command finished before returning result: {self!r}")) self.finished.set_result(None) @@ -1261,12 +1261,12 @@ def set_finished(self) -> None: def _cancel(self) -> None: if self.state != CommandState.CANCELLING and self.state != CommandState.DONE: - assert self.state == CommandState.ACTIVE + assert self.state == CommandState.ACTIVE, self.state self.state = CommandState.CANCELLING self.cancel() def _start(self) -> None: - assert self.state == CommandState.NEW + assert self.state == CommandState.NEW, self.state self.state = CommandState.ACTIVE try: self.check_initialized() @@ -1275,7 +1275,7 @@ def _start(self) -> None: self._handle_exception(err) def _line_received(self, line: str) -> None: - assert self.state in [CommandState.ACTIVE, CommandState.CANCELLING] + assert self.state in [CommandState.ACTIVE, CommandState.CANCELLING], self.state try: self.line_received(line) except EngineError as err: From 7299216641f5bd0434c06111608892617aa39147 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 31 Jul 2024 22:37:53 +0200 Subject: [PATCH 055/116] Immediately dispatch line/termination/finish (fixes #1049, fixes #1071) Avoids races between queued up lines and command finish callbacks. --- CHANGELOG.rst | 2 ++ chess/engine.py | 40 +++++++++++++++++++++++++++------------- test.py | 18 ++++++++++++++++++ 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 754b5d418..9a0a16e6c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -30,6 +30,8 @@ New features: Bugfixes: +* Fix unsolicited engine output may cause assertion errors with regard to + command states. * Fix handling of whitespace in UCI engine communication. * For ``chess.Board.epd()`` and ``chess.Board.set_epd()``, require that EPD opcodes start with a letter. diff --git a/chess/engine.py b/chess/engine.py index 302aa2bbd..ccd6894d2 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -883,7 +883,7 @@ def write(self, data: bytes) -> None: expectation, responses = self.expectations.popleft() assert expectation == line, f"expected {expectation}, got: {line}" if responses: - self.protocol.pipe_data_received(1, "\n".join(responses + [""]).encode("utf-8")) + self.protocol.loop.call_soon(self.protocol.pipe_data_received, 1, "\n".join(responses + [""]).encode("utf-8")) def get_pid(self) -> int: return id(self) @@ -934,12 +934,12 @@ def connection_lost(self, exc: Optional[Exception]) -> None: LOGGER.debug("%s: Connection lost (exit code: %d, error: %s)", self, code, exc) # Terminate commands. - if self.command is not None: - self.command._engine_terminated(code) - self.command = None - if self.next_command is not None: - self.next_command._engine_terminated(code) - self.next_command = None + command, self.command = self.command, None + next_command, self.next_command = self.next_command, None + if command: + command._engine_terminated(code) + if next_command: + next_command._engine_terminated(code) self.returncode.set_result(code) @@ -965,9 +965,9 @@ def pipe_data_received(self, fd: int, data: Union[bytes, str]) -> None: LOGGER.warning("%s: >> %r (%s)", self, bytes(line_bytes), err) else: if fd == 1: - self.loop.call_soon(self._line_received, line) + self._line_received(line) else: - self.loop.call_soon(self.error_line_received, line) + self.error_line_received(line) def error_line_received(self, line: str) -> None: LOGGER.warning("%s: stderr >> %s", self, line) @@ -998,7 +998,7 @@ async def communicate(self, command_factory: Callable[[Self], BaseCommand[T]]) - self.next_command = command - def previous_command_finished(_: Optional[asyncio.Future[None]]) -> None: + def previous_command_finished() -> None: self.command, self.next_command = self.next_command, None if self.command is not None: cmd = self.command @@ -1008,11 +1008,11 @@ def cancel_if_cancelled(result: asyncio.Future[T]) -> None: cmd._cancel() cmd.result.add_done_callback(cancel_if_cancelled) - cmd.finished.add_done_callback(previous_command_finished) cmd._start() + cmd.add_finished_callback(previous_command_finished) if self.command is None: - previous_command_finished(None) + previous_command_finished() elif not self.command.result.done(): self.command.result.cancel() elif not self.command.result.cancelled(): @@ -1228,6 +1228,17 @@ def __init__(self, engine: Protocol) -> None: self.result: asyncio.Future[T] = asyncio.Future() self.finished: asyncio.Future[None] = asyncio.Future() + self._finished_callbacks: List[Callable[[], None]] = [] + + def add_finished_callback(self, callback: Callable[[], None]) -> None: + self._finished_callbacks.append(callback) + self._dispatch_finished() + + def _dispatch_finished(self) -> None: + if self.finished.done(): + while self._finished_callbacks: + self._finished_callbacks.pop()() + def _engine_terminated(self, code: int) -> None: hint = ", binary not compatible with cpu?" if code in [-4, 0xc000001d] else "" exc = EngineTerminatedError(f"engine process died unexpectedly (exit code: {code}{hint})") @@ -1235,6 +1246,7 @@ def _engine_terminated(self, code: int) -> None: self.engine_terminated(exc) elif self.state == CommandState.CANCELLING: self.finished.set_result(None) + self._dispatch_finished() elif self.state == CommandState.NEW: self._handle_exception(exc) @@ -1251,13 +1263,15 @@ def _handle_exception(self, exc: Exception) -> None: if not self.finished.done(): self.finished.set_result(None) + self._dispatch_finished() def set_finished(self) -> None: assert self.state in [CommandState.ACTIVE, CommandState.CANCELLING], self.state if not self.result.done(): self.result.set_exception(EngineError(f"engine command finished before returning result: {self!r}")) - self.finished.set_result(None) self.state = CommandState.DONE + self.finished.set_result(None) + self._dispatch_finished() def _cancel(self) -> None: if self.state != CommandState.CANCELLING and self.state != CommandState.DONE: diff --git a/test.py b/test.py index 00d766a03..438628b98 100755 --- a/test.py +++ b/test.py @@ -3527,6 +3527,24 @@ async def main(): asyncio.run(main()) + def test_uci_output_after_command(self): + async def main(): + protocol = chess.engine.UciProtocol() + mock = chess.engine.MockTransport(protocol) + + mock.expect("uci", [ + "Arasan v24.0.0-10-g367aa9f Copyright 1994-2023 by Jon Dart.", + "All rights reserved.", + "id name Arasan v24.0.0-10-g367aa9f", + "uciok", + "info string out of do_all_pending, list size=0" + ]) + await protocol.initialize() + + mock.assert_done() + + asyncio.run(main()) + def test_hiarcs_bestmove(self): async def main(): protocol = chess.engine.UciProtocol() From 7fa1f1825bc504ad50724e848b0e982b526c9f87 Mon Sep 17 00:00:00 2001 From: Lukas Malte Monnerjahn Date: Thu, 5 Sep 2024 11:31:03 +0200 Subject: [PATCH 056/116] SVG: Align pieces, check mark, selected squares and arrows correctly when rendering a border #1103 --- chess/svg.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/chess/svg.py b/chess/svg.py index 2948b0b80..c665b171b 100644 --- a/chess/svg.py +++ b/chess/svg.py @@ -283,6 +283,7 @@ def board(board: Optional[chess.BaseBoard] = None, *, inner_border = 1 if borders and coordinates else 0 outer_border = 1 if borders else 0 margin = 15 if coordinates else 0 + board_offset = inner_border + margin + outer_border full_size = 2 * outer_border + 2 * margin + 2 * inner_border + 8 * SQUARE_SIZE svg = _svg(full_size, size) @@ -351,12 +352,12 @@ def board(board: Optional[chess.BaseBoard] = None, *, if coordinates: coord_color, coord_opacity = _select_color(colors, "coord") for file_index, file_name in enumerate(chess.FILE_NAMES): - x = (file_index if orientation else 7 - file_index) * SQUARE_SIZE + inner_border + margin + outer_border + x = (file_index if orientation else 7 - file_index) * SQUARE_SIZE + board_offset # Keep some padding here to separate the ascender from the border svg.append(_coord(file_name, x, 1, SQUARE_SIZE, margin, True, margin, color=coord_color, opacity=coord_opacity)) svg.append(_coord(file_name, x, full_size - outer_border - margin, SQUARE_SIZE, margin, True, margin, color=coord_color, opacity=coord_opacity)) for rank_index, rank_name in enumerate(chess.RANK_NAMES): - y = (7 - rank_index if orientation else rank_index) * SQUARE_SIZE + inner_border + margin + outer_border + y = (7 - rank_index if orientation else rank_index) * SQUARE_SIZE + board_offset svg.append(_coord(rank_name, 0, y, margin, SQUARE_SIZE, False, margin, color=coord_color, opacity=coord_opacity)) svg.append(_coord(rank_name, full_size - outer_border - margin, y, margin, SQUARE_SIZE, False, margin, color=coord_color, opacity=coord_opacity)) @@ -365,8 +366,8 @@ def board(board: Optional[chess.BaseBoard] = None, *, file_index = chess.square_file(square) rank_index = chess.square_rank(square) - x = (file_index if orientation else 7 - file_index) * SQUARE_SIZE + inner_border + margin + outer_border - y = (7 - rank_index if orientation else rank_index) * SQUARE_SIZE + inner_border + margin + outer_border + x = (file_index if orientation else 7 - file_index) * SQUARE_SIZE + board_offset + y = (7 - rank_index if orientation else rank_index) * SQUARE_SIZE + board_offset cls = ["square", "light" if chess.BB_LIGHT_SQUARES & bb else "dark"] if lastmove and square in [lastmove.from_square, lastmove.to_square]: @@ -406,8 +407,8 @@ def board(board: Optional[chess.BaseBoard] = None, *, file_index = chess.square_file(check) rank_index = chess.square_rank(check) - x = (file_index if orientation else 7 - file_index) * SQUARE_SIZE + margin - y = (7 - rank_index if orientation else rank_index) * SQUARE_SIZE + margin + x = (file_index if orientation else 7 - file_index) * SQUARE_SIZE + board_offset + y = (7 - rank_index if orientation else rank_index) * SQUARE_SIZE + board_offset ET.SubElement(svg, "rect", _attrs({ "x": x, @@ -423,8 +424,8 @@ def board(board: Optional[chess.BaseBoard] = None, *, file_index = chess.square_file(square) rank_index = chess.square_rank(square) - x = (file_index if orientation else 7 - file_index) * SQUARE_SIZE + margin - y = (7 - rank_index if orientation else rank_index) * SQUARE_SIZE + margin + x = (file_index if orientation else 7 - file_index) * SQUARE_SIZE + board_offset + y = (7 - rank_index if orientation else rank_index) * SQUARE_SIZE + board_offset if board is not None: piece = board.piece_at(square) @@ -463,10 +464,10 @@ def board(board: Optional[chess.BaseBoard] = None, *, head_file = chess.square_file(head) head_rank = chess.square_rank(head) - xtail = outer_border + margin + inner_border + (tail_file + 0.5 if orientation else 7.5 - tail_file) * SQUARE_SIZE - ytail = outer_border + margin + inner_border + (7.5 - tail_rank if orientation else tail_rank + 0.5) * SQUARE_SIZE - xhead = outer_border + margin + inner_border + (head_file + 0.5 if orientation else 7.5 - head_file) * SQUARE_SIZE - yhead = outer_border + margin + inner_border + (7.5 - head_rank if orientation else head_rank + 0.5) * SQUARE_SIZE + xtail = board_offset + (tail_file + 0.5 if orientation else 7.5 - tail_file) * SQUARE_SIZE + ytail = board_offset + (7.5 - tail_rank if orientation else tail_rank + 0.5) * SQUARE_SIZE + xhead = board_offset + (head_file + 0.5 if orientation else 7.5 - head_file) * SQUARE_SIZE + yhead = board_offset + (7.5 - head_rank if orientation else head_rank + 0.5) * SQUARE_SIZE if (head_file, head_rank) == (tail_file, tail_rank): ET.SubElement(svg, "circle", _attrs({ From 0e7fabc8ae0127a994aaece31e3c69a510cef527 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 27 Sep 2024 17:37:16 +0200 Subject: [PATCH 057/116] Test reading PGN with UTF-8 BOM --- data/pgn/utf8-bom.pgn | 26 ++++++++++++++++++++++++++ test.py | 12 ++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 data/pgn/utf8-bom.pgn diff --git a/data/pgn/utf8-bom.pgn b/data/pgn/utf8-bom.pgn new file mode 100644 index 000000000..665e7775a --- /dev/null +++ b/data/pgn/utf8-bom.pgn @@ -0,0 +1,26 @@ +[Event "A"] +[Site "?"] +[Date "2024.04.25"] +[Round "?"] +[White "White vs 1...c5"] +[Black "?"] +[Result "*"] +[ECO "A00"] +[PlyCount "0"] +[SourceVersionDate "2024.04.25"] + + * + +[Event "B"] +[Site "?"] +[Date "2024.04.25"] +[Round "?"] +[White "White vs 1...c5"] +[Black "?"] +[Result "*"] +[ECO "A00"] +[PlyCount "0"] +[SourceVersionDate "2024.04.25"] + + * + diff --git a/test.py b/test.py index 438628b98..6102eabdb 100755 --- a/test.py +++ b/test.py @@ -2947,6 +2947,18 @@ def end_variation(self): game = chess.pgn.read_game(io.StringIO(pgn)).accept(BlackVariationsOnly()) self.assertEqual(game.accept(chess.pgn.StringExporter(headers=False)), expected_pgn) + def test_utf8_bom(self): + not_utf8_sig = "utf-8" + with open("data/pgn/utf8-bom.pgn", encoding=not_utf8_sig) as pgn: + game = chess.pgn.read_game(pgn) + self.assertEqual(game.headers["Event"], "A") + + game = chess.pgn.read_game(pgn) + self.assertEqual(game.headers["Event"], "B") + + game = chess.pgn.read_game(pgn) + self.assertEqual(game, None) + @unittest.skipIf(sys.platform == "win32" and (3, 8, 0) <= sys.version_info < (3, 8, 1), "https://bugs.python.org/issue34679") class EngineTestCase(unittest.TestCase): From c0d3c9171d22d0168a6cf7a584cdcf50909832e6 Mon Sep 17 00:00:00 2001 From: evidencebp Date: Thu, 3 Oct 2024 14:33:20 +0300 Subject: [PATCH 058/116] chess\__init__.py superfluous-parens There are two such alerts in this file. One has two terms and the parentheses do not contribute to readability. The other term is longer and the parenthesis help to readability and therefore are kept. --- chess/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chess/__init__.py b/chess/__init__.py index 55bd4cad2..9d6e869fe 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -3402,7 +3402,7 @@ def clean_castling_rights(self) -> Bitboard: if white_h_side and msb(white_h_side) < msb(white_king_mask): white_h_side = 0 - black_a_side = (black_castling & -black_castling) + black_a_side = black_castling & -black_castling black_h_side = BB_SQUARES[msb(black_castling)] if black_castling else BB_EMPTY if black_a_side and msb(black_a_side) > msb(black_king_mask): From 30d991046908ce7c5a7929987c1c2081f68641b4 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 4 Oct 2024 18:33:54 +0200 Subject: [PATCH 059/116] Prepare 1.11.0 --- CHANGELOG.rst | 4 ++-- chess/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9a0a16e6c..296079f24 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,8 +1,8 @@ Changelog for python-chess ========================== -Upcoming in the next release ----------------------------- +New in v1.11.0 (4th Oct 2024) +----------------------------- Changes: diff --git a/chess/__init__.py b/chess/__init__.py index 9d6e869fe..147f24be7 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -11,7 +11,7 @@ __email__ = "niklas.fiekas@backscattering.de" -__version__ = "1.10.0" +__version__ = "1.11.0" import collections import copy From 46c28883f7846f87f419c5c83e7c61e3cdea5d36 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 4 Oct 2024 18:44:53 +0200 Subject: [PATCH 060/116] Revert "Removed unnecessary indentation blocks and updated conditional statements." This reverts commit 4a6dd9a89b7810312ab4ea1cf255d1a501ae09ca. --- release.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/release.py b/release.py index f2f7d81fa..d27ddf04e 100755 --- a/release.py +++ b/release.py @@ -10,7 +10,7 @@ def system(command): print(command) exit_code = os.system(command) - if exit_code: + if exit_code != 0: sys.exit(exit_code) @@ -21,7 +21,7 @@ def check_git(): system("git fetch origin") behind = int(subprocess.check_output(["git", "rev-list", "--count", "master..origin/master"])) - if behind: + if behind > 0: print(f"master is {behind} commit(s) behind origin/master") sys.exit(1) @@ -65,14 +65,15 @@ def tag_and_push(): release_txt.write(headline + os.linesep) for line in changelog_file: - if line.startswith("-------") and not first_section: - first_section = True + if not first_section: + if line.startswith("-------"): + first_section = True else: if line.startswith("-------"): break - - if not prev_line.startswith("------"): - release_txt.write(prev_line) + else: + if not prev_line.startswith("------"): + release_txt.write(prev_line) prev_line = line From 08697b298d87e4fa01842bd82af96d71cb972c90 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 4 Oct 2024 18:57:48 +0200 Subject: [PATCH 061/116] Fix twine usage, stop uploading wheels --- release.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release.py b/release.py index d27ddf04e..4057ab785 100755 --- a/release.py +++ b/release.py @@ -97,9 +97,9 @@ def tag_and_push(): def pypi(): print("--- PYPI ---------------------------------------------------------") system("rm -rf build") - system("python3 setup.py sdist bdist_wheel") + system("python3 setup.py sdist") system("twine check dist/*") - system("twine upload --skip-existing") + system("twine upload --skip-existing dist/*") def github_release(tagname): From 611221de07fa7679b5d00b712cf8472ce66e6b38 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Mon, 7 Oct 2024 20:08:23 -0700 Subject: [PATCH 062/116] Fix error when parsing UCI options The regex that splits a UCI option into parts did not include word boundries, so words like "mini" would be split into "min" and "i", leading to parse errors. This commit puts word boundries into the regex to fix this. Fixes #1112 --- chess/engine.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chess/engine.py b/chess/engine.py index ccd6894d2..270e31e71 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -1367,7 +1367,8 @@ def _option(self, arg: str) -> None: var = [] parameters = list(option_parts.keys()) + ['var'] - option_regex = fr"\s*({'|'.join(parameters)})\s*" + inner_regex = '|'.join([fr"\b{parameter}\b" for parameter in parameters]) + option_regex = fr"\s*({inner_regex})\s*" for token in re.split(option_regex, arg.strip()): if token == "var" or (token in option_parts and not option_parts[token]): current_parameter = token From 54b18de726e0f1d06f89f35104c66ddeb527fac7 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Mon, 7 Oct 2024 22:38:29 -0700 Subject: [PATCH 063/116] Add test for options fix Test uses fairy-stockfish since that engine revealed the original bug. --- test.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test.py b/test.py index 2a1df1c85..9903b208d 100755 --- a/test.py +++ b/test.py @@ -3142,6 +3142,11 @@ def test_sf_quit(self): with self.assertRaises(chess.engine.EngineTerminatedError), engine: engine.ping() + @catchAndSkip(FileNotFoundError, "need fairy-stockfish") + def test_fairy_sf_initialize(self): + with chess.engine.SimpleEngine.popen_uci("fairy-stockfish", setpgrp=True, debug=True): + pass + @catchAndSkip(FileNotFoundError, "need crafty") def test_crafty_play_to_mate(self): logging.disable(logging.WARNING) From ebb687c5dab328d91a055b1f63dabf6c28739ed2 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Mon, 7 Oct 2024 22:58:59 -0700 Subject: [PATCH 064/116] Make test functional by installing fairy-stockfish --- .github/workflows/setup-ubuntu-latest.sh | 3 +++ .github/workflows/setup-windows-latest.sh | 6 +++++- tox.ini | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/setup-ubuntu-latest.sh b/.github/workflows/setup-ubuntu-latest.sh index fae35b61a..10b8d1115 100755 --- a/.github/workflows/setup-ubuntu-latest.sh +++ b/.github/workflows/setup-ubuntu-latest.sh @@ -6,6 +6,9 @@ sudo apt-get install -y stockfish # Crafty sudo apt-get install -y crafty +# Fairy-stockfish +sudo apt-get install -y fairy-stockfish + # Gaviota libgtb git clone https://github.com/michiguel/Gaviota-Tablebases.git --depth 1 cd Gaviota-Tablebases diff --git a/.github/workflows/setup-windows-latest.sh b/.github/workflows/setup-windows-latest.sh index 815eb0db2..48037cbef 100755 --- a/.github/workflows/setup-windows-latest.sh +++ b/.github/workflows/setup-windows-latest.sh @@ -1,6 +1,6 @@ #!/bin/sh -e -echo Download ... +echo Download stockfish ... choco install wget wget https://github.com/official-stockfish/Stockfish/releases/download/sf_16/stockfish-windows-x86-64-avx2.zip @@ -10,3 +10,7 @@ echo Unzip .. echo Setup path ... mv stockfish-windows-x86-64-avx2.exe stockfish.exe pwd >> $GITHUB_PATH + +echo Download fairy-stockfish ... +wget https://github.com/fairy-stockfish/Fairy-Stockfish/releases/latest/download/fairy-stockfish-largeboard_x86-64.exe +mv fairy-stockfish-largeboard_x86-64.exe fairy-stockfish.exe diff --git a/tox.ini b/tox.ini index f629723cd..8d8033676 100644 --- a/tox.ini +++ b/tox.ini @@ -6,6 +6,7 @@ passenv = LD_LIBRARY_PATH whitelist_externals = stockfish crafty + fairy-stockfish commands = python test.py --verbose python -m doctest README.rst --verbose From c0ac01b784bf1c63abb325424ea9e7bb2cb3f44e Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Mon, 7 Oct 2024 23:13:27 -0700 Subject: [PATCH 065/116] Add test using MockTransport --- test.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test.py b/test.py index 9903b208d..e490daa90 100755 --- a/test.py +++ b/test.py @@ -3147,6 +3147,21 @@ def test_fairy_sf_initialize(self): with chess.engine.SimpleEngine.popen_uci("fairy-stockfish", setpgrp=True, debug=True): pass + def test_uci_option_parse(self): + async def main(): + protocol = chess.engine.UciProtocol() + mock = chess.engine.MockTransport(protocol) + + mock.expect("uci", ["option name UCI_Variant type combo default chess var bughouse var chess var mini var minishogi var threekings", "uciok"]) + await protocol.initialize() + mock.assert_done() + + mock.expect("isready", ["readyok"]) + await protocol.ping() + mock.assert_done() + + asyncio.run(main()) + @catchAndSkip(FileNotFoundError, "need crafty") def test_crafty_play_to_mate(self): logging.disable(logging.WARNING) From fa4f0148f683c8e59ffa762f93045f9edfc69994 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Tue, 8 Oct 2024 22:07:21 +0200 Subject: [PATCH 066/116] Merge pull request #1113 from MarkZH/options-fix Fix error when parsing UCI options --- .github/workflows/setup-ubuntu-latest.sh | 3 +++ .github/workflows/setup-windows-latest.sh | 6 +++++- chess/engine.py | 3 ++- test.py | 20 ++++++++++++++++++++ tox.ini | 1 + 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/.github/workflows/setup-ubuntu-latest.sh b/.github/workflows/setup-ubuntu-latest.sh index fae35b61a..10b8d1115 100755 --- a/.github/workflows/setup-ubuntu-latest.sh +++ b/.github/workflows/setup-ubuntu-latest.sh @@ -6,6 +6,9 @@ sudo apt-get install -y stockfish # Crafty sudo apt-get install -y crafty +# Fairy-stockfish +sudo apt-get install -y fairy-stockfish + # Gaviota libgtb git clone https://github.com/michiguel/Gaviota-Tablebases.git --depth 1 cd Gaviota-Tablebases diff --git a/.github/workflows/setup-windows-latest.sh b/.github/workflows/setup-windows-latest.sh index 815eb0db2..48037cbef 100755 --- a/.github/workflows/setup-windows-latest.sh +++ b/.github/workflows/setup-windows-latest.sh @@ -1,6 +1,6 @@ #!/bin/sh -e -echo Download ... +echo Download stockfish ... choco install wget wget https://github.com/official-stockfish/Stockfish/releases/download/sf_16/stockfish-windows-x86-64-avx2.zip @@ -10,3 +10,7 @@ echo Unzip .. echo Setup path ... mv stockfish-windows-x86-64-avx2.exe stockfish.exe pwd >> $GITHUB_PATH + +echo Download fairy-stockfish ... +wget https://github.com/fairy-stockfish/Fairy-Stockfish/releases/latest/download/fairy-stockfish-largeboard_x86-64.exe +mv fairy-stockfish-largeboard_x86-64.exe fairy-stockfish.exe diff --git a/chess/engine.py b/chess/engine.py index ccd6894d2..270e31e71 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -1367,7 +1367,8 @@ def _option(self, arg: str) -> None: var = [] parameters = list(option_parts.keys()) + ['var'] - option_regex = fr"\s*({'|'.join(parameters)})\s*" + inner_regex = '|'.join([fr"\b{parameter}\b" for parameter in parameters]) + option_regex = fr"\s*({inner_regex})\s*" for token in re.split(option_regex, arg.strip()): if token == "var" or (token in option_parts and not option_parts[token]): current_parameter = token diff --git a/test.py b/test.py index 6102eabdb..9e2f9d166 100755 --- a/test.py +++ b/test.py @@ -3134,6 +3134,26 @@ def test_sf_quit(self): with self.assertRaises(chess.engine.EngineTerminatedError), engine: engine.ping() + @catchAndSkip(FileNotFoundError, "need fairy-stockfish") + def test_fairy_sf_initialize(self): + with chess.engine.SimpleEngine.popen_uci("fairy-stockfish", setpgrp=True, debug=True): + pass + + def test_uci_option_parse(self): + async def main(): + protocol = chess.engine.UciProtocol() + mock = chess.engine.MockTransport(protocol) + + mock.expect("uci", ["option name UCI_Variant type combo default chess var bughouse var chess var mini var minishogi var threekings", "uciok"]) + await protocol.initialize() + mock.assert_done() + + mock.expect("isready", ["readyok"]) + await protocol.ping() + mock.assert_done() + + asyncio.run(main()) + @catchAndSkip(FileNotFoundError, "need crafty") def test_crafty_play_to_mate(self): logging.disable(logging.WARNING) diff --git a/tox.ini b/tox.ini index f629723cd..8d8033676 100644 --- a/tox.ini +++ b/tox.ini @@ -6,6 +6,7 @@ passenv = LD_LIBRARY_PATH whitelist_externals = stockfish crafty + fairy-stockfish commands = python test.py --verbose python -m doctest README.rst --verbose From ffc6da935642654ee13eda3bb649a8748531b960 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 9 Oct 2024 20:24:42 +0200 Subject: [PATCH 067/116] Prepare 1.11.1 --- CHANGELOG.rst | 8 ++++++++ chess/__init__.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 296079f24..05b899a20 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,14 @@ Changelog for python-chess ========================== +New in v1.11.1 (9th Oct 2024) +----------------------------- + +Bugfixes: + +* ``chess.engine``: Fix parsing of UCI options containing containing ``name``, + ``type``, ``default``, ``min``, or ``max``, e.g., ``mini``. + New in v1.11.0 (4th Oct 2024) ----------------------------- diff --git a/chess/__init__.py b/chess/__init__.py index 147f24be7..a6a66500e 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -11,7 +11,7 @@ __email__ = "niklas.fiekas@backscattering.de" -__version__ = "1.11.0" +__version__ = "1.11.1" import collections import copy From 7f123cb5dd858978de4a32b7d2c8f8ea71a3552c Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 9 Oct 2024 20:35:10 +0200 Subject: [PATCH 068/116] Remove chess.svg.board(..., flipped), use orientation (#659) --- chess/svg.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/chess/svg.py b/chess/svg.py index c665b171b..7e8facf99 100644 --- a/chess/svg.py +++ b/chess/svg.py @@ -227,7 +227,6 @@ def board(board: Optional[chess.BaseBoard] = None, *, size: Optional[int] = None, coordinates: bool = True, colors: Dict[str, str] = {}, - flipped: bool = False, borders: bool = False, style: Optional[str] = None) -> str: """ @@ -255,7 +254,6 @@ def board(board: Optional[chess.BaseBoard] = None, *, ``outer border``, ``arrow green``, ``arrow blue``, ``arrow red``, and ``arrow yellow``. Values should look like ``#ffce9e`` (opaque), or ``#15781B80`` (transparent). - :param flipped: Pass ``True`` to flip the board. :param borders: Pass ``True`` to enable a border around the board and, (if *coordinates* is enabled) the coordinate margin. :param style: A CSS stylesheet to include in the SVG image. @@ -275,11 +273,7 @@ def board(board: Optional[chess.BaseBoard] = None, *, .. image:: ../docs/Ne4.svg :alt: 8/8/8/8/4N3/8/8/8 - - .. deprecated:: 1.1 - Use *orientation* with a color instead of the *flipped* toggle. """ - orientation ^= flipped inner_border = 1 if borders and coordinates else 0 outer_border = 1 if borders else 0 margin = 15 if coordinates else 0 From f2b0452389c4272948b253ac6266eb89ac85c179 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 9 Oct 2024 20:37:08 +0200 Subject: [PATCH 069/116] Remove deprecated chess.pgn.BaseVisitor.parse_san() (#659) --- chess/pgn.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index bded9ae5d..eddb6bc52 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -1104,22 +1104,6 @@ def begin_parse_san(self, board: chess.Board, san: str) -> Optional[SkipType]: """ pass - def parse_san(self, board: chess.Board, san: str) -> chess.Move: - """ - When the visitor is used by a parser, this is called to parse a move - in standard algebraic notation. - - You can override the default implementation to work around specific - quirks of your input format. - - .. deprecated:: 1.1 - This method is very limited, because it is only called on moves - that the parser recognizes in the first place. Instead of adding - workarounds here, please report common quirks so that - they can be handled for everyone. - """ - return board.parse_san(san) - def visit_move(self, board: chess.Board, move: chess.Move) -> None: """ Called for each move. @@ -1751,7 +1735,7 @@ def read_game(handle: TextIO, *, Visitor: Any = GameBuilder) -> Any: # Parse SAN tokens. if visitor.begin_parse_san(board_stack[-1], token) is not SKIP: try: - move = visitor.parse_san(board_stack[-1], token) + move = board_stack[-1].parse_san(token) except ValueError as error: visitor.handle_error(error) skip_variation_depth = 1 From b2657ebc4768e2815cb8ddeb8c9ed13109332e15 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 9 Oct 2024 20:41:43 +0200 Subject: [PATCH 070/116] Remove deprecated chess.engine.Wdl tuple behavior (#659) --- chess/engine.py | 45 +-------------------------------------------- test.py | 2 +- 2 files changed, 2 insertions(+), 45 deletions(-) diff --git a/chess/engine.py b/chess/engine.py index 270e31e71..64a3926b7 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -720,16 +720,11 @@ def __str__(self) -> str: MateGiven = MateGivenType() +@dataclasses.dataclass class PovWdl: """ Relative :class:`win/draw/loss statistics ` and the point of view. - - .. deprecated:: 1.2 - Behaves like a tuple - ``(wdl.relative.wins, wdl.relative.draws, wdl.relative.losses)`` - for backwards compatibility. But it is recommended to use the provided - fields and methods instead. """ relative: Wdl @@ -738,10 +733,6 @@ class PovWdl: turn: Color """The point of view (``chess.WHITE`` or ``chess.BLACK``).""" - def __init__(self, relative: Wdl, turn: Color) -> None: - self.relative = relative - self.turn = turn - def white(self) -> Wdl: """Gets the :class:`~chess.engine.Wdl` from White's point of view.""" return self.pov(chess.WHITE) @@ -763,30 +754,6 @@ def __bool__(self) -> bool: def __repr__(self) -> str: return "PovWdl({!r}, {})".format(self.relative, "WHITE" if self.turn else "BLACK") - # Unfortunately in python-chess v1.1.0, info["wdl"] was a simple tuple - # of the relative permille values, so we have to support __iter__, - # __len__, __getitem__, and equality comparisons with other tuples. - # Never mind the ordering, because that's not a sensible operation, anyway. - - def __iter__(self) -> Iterator[int]: - yield self.relative.wins - yield self.relative.draws - yield self.relative.losses - - def __len__(self) -> int: - return 3 - - def __getitem__(self, idx: int) -> int: - return (self.relative.wins, self.relative.draws, self.relative.losses)[idx] - - def __eq__(self, other: object) -> bool: - if isinstance(other, PovWdl): - return self.white() == other.white() - elif isinstance(other, tuple): - return (self.relative.wins, self.relative.draws, self.relative.losses) == other - else: - return NotImplemented - @dataclasses.dataclass class Wdl: @@ -830,16 +797,6 @@ def expectation(self) -> float: def __bool__(self) -> bool: return bool(self.total()) - def __iter__(self) -> Iterator[int]: - yield self.wins - yield self.draws - yield self.losses - - def __reversed__(self) -> Iterator[int]: - yield self.losses - yield self.draws - yield self.wins - def __pos__(self) -> Wdl: return self diff --git a/test.py b/test.py index e490daa90..68904a50e 100755 --- a/test.py +++ b/test.py @@ -3540,7 +3540,7 @@ def test_uci_info(self): # WDL (activated with UCI_ShowWDL). info = chess.engine._parse_uci_info("depth 1 seldepth 2 time 16 nodes 1 score cp 72 wdl 249 747 4 hashfull 0 nps 400 tbhits 0 multipv 1", board) - self.assertEqual(info["wdl"], (249, 747, 4)) + self.assertEqual(info["wdl"].white(), chess.engine.Wdl(249, 747, 4)) def test_uci_result(self): async def main(): From 6228bac55b8e680362f35e69c1c72a4d53d00cf4 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 12 Oct 2024 20:50:29 +0200 Subject: [PATCH 071/116] Update Sphinx --- docs/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 7516f64de..c04e3554a 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,3 @@ -Sphinx==7.3.6 +Sphinx==8.1.2 sphinxcontrib-jquery==4.1 -sphinx-rtd-theme==1.3.0 +sphinx-rtd-theme==3.0.1 From e2041699939d7ae08811fe7a1391e8910d5ec62a Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sun, 13 Oct 2024 11:12:49 +0200 Subject: [PATCH 072/116] Towards passing pyright --- chess/__init__.py | 10 +++++++--- chess/engine.py | 2 +- chess/syzygy.py | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/chess/__init__.py b/chess/__init__.py index 147f24be7..4f5791f7c 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -1218,6 +1218,8 @@ def _set_chess960_pos(self, scharnagl: int) -> None: n, bb = divmod(n, 4) n, q = divmod(n, 6) + n1 = 0 + n2 = 0 for n1 in range(0, 4): n2 = n + (3 - n1) * (4 - n1) // 2 - 5 if n1 < n2 and 1 <= n2 <= 4: @@ -2394,7 +2396,7 @@ def push(self, move: Move) -> None: elif move.to_square == ep_square and abs(diff) in [7, 9] and not captured_piece_type: # Remove pawns captured en passant. down = -8 if self.turn == WHITE else 8 - capture_square = ep_square + down + capture_square = move.to_square + down captured_piece_type = self._remove_piece_at(capture_square) # Promotion. @@ -3166,6 +3168,8 @@ def parse_san(self, san: str) -> Move: # Filter by original square. from_mask = BB_ALL + from_file = None + from_rank = None if match.group(2): from_file = FILE_NAMES.index(match.group(2)) from_mask &= BB_FILES[from_file] @@ -3177,7 +3181,7 @@ def parse_san(self, san: str) -> Move: if match.group(1): piece_type = PIECE_SYMBOLS.index(match.group(1).lower()) from_mask &= self.pieces_mask(piece_type, self.turn) - elif match.group(2) and match.group(3): + elif from_file is not None and from_rank is not None: # Allow fully specified moves, even if they are not pawn moves, # including castling moves. move = self.find_move(square(from_file, from_rank), to_square, promotion) @@ -3189,7 +3193,7 @@ def parse_san(self, san: str) -> Move: from_mask &= self.pawns # Do not allow pawn captures if file is not specified. - if not match.group(2): + if from_file is None: from_mask &= BB_FILES[square_file(to_square)] # Match legal moves. diff --git a/chess/engine.py b/chess/engine.py index 64a3926b7..c961d0da1 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -826,7 +826,7 @@ def get_pipe_transport(self, fd: int) -> Optional[asyncio.BaseTransport]: assert fd == 0, f"expected 0 for stdin, got {fd}" return self - def write(self, data: bytes) -> None: + def write(self, data: bytes | bytearray | memoryview) -> None: self.stdin_buffer.extend(data) while b"\n" in self.stdin_buffer: line_bytes, self.stdin_buffer = self.stdin_buffer.split(b"\n", 1) diff --git a/chess/syzygy.py b/chess/syzygy.py index 77a6eeded..89f8c2596 100644 --- a/chess/syzygy.py +++ b/chess/syzygy.py @@ -780,6 +780,7 @@ def encode_piece(self, norm: List[int], pos: List[chess.Square], factor: List[in for i in range(n): pos[i] ^= 0x38 + i = 0 for i in range(n): if offdiag(pos[i]): break From c42749dee7e06f1215ad159d58452d571810b8db Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sun, 13 Oct 2024 11:49:05 +0200 Subject: [PATCH 073/116] Make chess.engine.Protocol.options an abstract property --- chess/engine.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/chess/engine.py b/chess/engine.py index c961d0da1..3f1e0e399 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -852,9 +852,6 @@ def get_returncode(self) -> Optional[int]: class Protocol(asyncio.SubprocessProtocol, metaclass=abc.ABCMeta): """Protocol for communicating with a chess engine process.""" - options: MutableMapping[str, Option] - """Dictionary of available options.""" - id: Dict[str, str] """ Dictionary of information about the engine. Common keys are ``name`` @@ -981,6 +978,11 @@ def __repr__(self) -> str: pid = self.transport.get_pid() if self.transport is not None else "?" return f"<{type(self).__name__} (pid={pid})>" + @property + @abc.abstractmethod + def options(self) -> MutableMapping[str, Option]: + """Dictionary of available options.""" + @abc.abstractmethod async def initialize(self) -> None: """Initializes the engine.""" @@ -1281,7 +1283,7 @@ class UciProtocol(Protocol): def __init__(self) -> None: super().__init__() - self.options: UciOptionMap[Option] = UciOptionMap() + self._options: UciOptionMap[Option] = UciOptionMap() self.config: UciOptionMap[ConfigValue] = UciOptionMap() self.target_config: UciOptionMap[ConfigValue] = UciOptionMap() self.id = {} @@ -1291,6 +1293,11 @@ def __init__(self) -> None: self.may_ponderhit: Optional[chess.Board] = None self.ponderhit = False + @property + @override + def options(self) -> UciOptionMap[Option]: + return self._options + async def initialize(self) -> None: class UciInitializeCommand(BaseCommand[None]): def __init__(self, engine: UciProtocol): @@ -1939,7 +1946,7 @@ def __init__(self) -> None: super().__init__() self.features: Dict[str, Union[int, str]] = {} self.id = {} - self.options = { + self._options = { "random": Option("random", "check", False, None, None, None), "computer": Option("computer", "check", False, None, None, None), "name": Option("name", "string", "", None, None, None), @@ -1953,6 +1960,11 @@ def __init__(self) -> None: self.clock_id: object = None self.first_game = True + @property + @override + def options(self) -> Dict[str, Option]: + return self._options + async def initialize(self) -> None: class XBoardInitializeCommand(BaseCommand[None]): def __init__(self, engine: XBoardProtocol): From 7553d4115f69368e2d7bf07a90773cea128d7d11 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sun, 13 Oct 2024 12:35:01 +0200 Subject: [PATCH 074/116] Make chess.pgn.GameNode pass pyright subtyping check --- chess/pgn.py | 65 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index eddb6bc52..20e95de50 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -16,7 +16,11 @@ from chess import Color, Square if typing.TYPE_CHECKING: - from typing_extensions import Self + from typing_extensions import Self, override +else: + F = typing.TypeVar("F", bound=Callable[..., Any]) + def override(fn: F, /) -> F: + return fn LOGGER = logging.getLogger(__name__) @@ -192,15 +196,6 @@ def __init__(self, node: ChildNode, *, is_variation: bool = False, sidelines: bo class GameNode(abc.ABC): - parent: Optional[GameNode] - """The parent node or ``None`` if this is the root node of the game.""" - - move: Optional[chess.Move] - """ - The move leading to this node or ``None`` if this is the root node of the - game. - """ - variations: List[ChildNode] """A list of child nodes.""" @@ -215,8 +210,6 @@ class GameNode(abc.ABC): nags: Set[int] def __init__(self, *, comment: Union[str, list[str]] = "") -> None: - self.parent = None - self.move = None self.variations = [] self.comments = _standardize_comments(comment) @@ -225,6 +218,19 @@ def __init__(self, *, comment: Union[str, list[str]] = "") -> None: self.starting_comments = [] self.nags = set() + @property + @abc.abstractmethod + def parent(self) -> Optional[GameNode]: + """The parent node or ``None`` if this is the root node of the game.""" + + @property + @abc.abstractmethod + def move(self) -> Optional[chess.Move]: + """ + The move leading to this node or ``None`` if this is the root node of + the game. + """ + @abc.abstractmethod def board(self) -> chess.Board: """ @@ -658,12 +664,6 @@ class ChildNode(GameNode): Extends :class:`~chess.pgn.GameNode`. """ - parent: GameNode - """The parent node.""" - - move: chess.Move - """The move leading to this node.""" - starting_comments: list[str] """ A comment for the start of a variation. Only nodes that @@ -680,13 +680,25 @@ class ChildNode(GameNode): def __init__(self, parent: GameNode, move: chess.Move, *, comment: Union[str, list[str]] = "", starting_comment: Union[str, list[str]] = "", nags: Iterable[int] = []) -> None: super().__init__(comment=comment) - self.parent = parent - self.move = move + self._parent = parent + self._move = move self.parent.variations.append(self) self.nags.update(nags) self.starting_comments = _standardize_comments(starting_comment) + @property + @override + def parent(self) -> GameNode: + """The parent node.""" + return self._parent + + @property + @override + def move(self) -> chess.Move: + """The move leading to this node.""" + return self._move + def board(self) -> chess.Board: stack: List[chess.Move] = [] node: GameNode = self @@ -838,9 +850,21 @@ def __init__(self, headers: Optional[Union[Mapping[str, str], Iterable[Tuple[str self.headers = Headers(headers) self.errors = [] + @property + @override + def parent(self) -> None: + return None + + @property + @override + def move(self) -> None: + return None + + @override def board(self) -> chess.Board: return self.headers.board() + @override def ply(self) -> int: # Optimization: Parse FEN only for custom starting positions. return self.board().ply() if "FEN" in self.headers else 0 @@ -873,6 +897,7 @@ def setup(self, board: Union[chess.Board, str]) -> None: else: self.headers.pop("Variant", None) + @override def accept(self, visitor: BaseVisitor[ResultT]) -> ResultT: """ Traverses the game in PGN order using the given *visitor*. Returns From f7736478cbe6e99bb77a7719bfafa79e9a40eef0 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sun, 13 Oct 2024 12:35:28 +0200 Subject: [PATCH 075/116] Initialize board_stack even on skip_game path --- chess/pgn.py | 1 + 1 file changed, 1 insertion(+) diff --git a/chess/pgn.py b/chess/pgn.py index 20e95de50..11d7aa760 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -1575,6 +1575,7 @@ def read_game(handle: TextIO, *, Visitor: Any = GameBuilder) -> Any: skipping_game = False managed_headers: Optional[Headers] = None unmanaged_headers: Optional[Headers] = None + board_stack: List[chess.Board] = [] # Ignore leading empty lines and comments. line = handle.readline().lstrip("\ufeff") From 0c8fed280f5d0ea237dc5a667081665d65412643 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sun, 13 Oct 2024 12:42:13 +0200 Subject: [PATCH 076/116] Fix builder constructor typing --- chess/pgn.py | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/chess/pgn.py b/chess/pgn.py index 11d7aa760..f40980d48 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -699,6 +699,7 @@ def move(self) -> chess.Move: """The move leading to this node.""" return self._move + @override def board(self) -> chess.Board: stack: List[chess.Move] = [] node: GameNode = self @@ -714,6 +715,7 @@ def board(self) -> chess.Board: return board + @override def ply(self) -> int: ply = 0 node: GameNode = self @@ -744,6 +746,7 @@ def uci(self, *, chess960: Optional[bool] = None) -> str: """ return self.parent.board().uci(self.move, chess960=chess960) + @override def end(self) -> ChildNode: """ Follows the main variation to the end and returns the last node. @@ -801,6 +804,7 @@ def _accept(self, parent_board: chess.Board, visitor: BaseVisitor[ResultT], *, s else: stack.pop() + @override def accept(self, visitor: BaseVisitor[ResultT]) -> ResultT: self._accept(self.parent.board(), visitor, sidelines=False) return visitor.result() @@ -1192,10 +1196,11 @@ class GameBuilder(BaseVisitor[GameT]): @typing.overload def __init__(self: GameBuilder[Game]) -> None: ... @typing.overload - def __init__(self: GameBuilder[GameT], *, Game: Type[GameT]) -> None: ... + def __init__(self, *, Game: Type[GameT]) -> None: ... def __init__(self, *, Game: Any = Game) -> None: self.Game = Game + @override def begin_game(self) -> None: self.game: GameT = self.Game() @@ -1203,28 +1208,35 @@ def begin_game(self) -> None: self.starting_comments: list[str] = [] self.in_variation = False + @override def begin_headers(self) -> Headers: return self.game.headers + @override def visit_header(self, tagname: str, tagvalue: str) -> None: self.game.headers[tagname] = tagvalue + @override def visit_nag(self, nag: int) -> None: self.variation_stack[-1].nags.add(nag) + @override def begin_variation(self) -> None: parent = self.variation_stack[-1].parent assert parent is not None, "begin_variation called, but root node on top of stack" self.variation_stack.append(parent) self.in_variation = False + @override def end_variation(self) -> None: self.variation_stack.pop() + @override def visit_result(self, result: str) -> None: if self.game.headers.get("Result", "*") == "*": self.game.headers["Result"] = result + @override def visit_comment(self, comment: Union[str, list[str]]) -> None: comments = _standardize_comments(comment) if self.in_variation or (self.variation_stack[-1].parent is None and self.variation_stack[-1].is_end()): @@ -1238,12 +1250,14 @@ def visit_comment(self, comment: Union[str, list[str]]) -> None: self.starting_comments.extend(comments) self.starting_comments = list(filter(None, self.starting_comments)) + @override def visit_move(self, board: chess.Board, move: chess.Move) -> None: self.variation_stack[-1] = self.variation_stack[-1].add_variation(move) self.variation_stack[-1].starting_comments = self.starting_comments self.starting_comments = [] self.in_variation = True + @override def handle_error(self, error: Exception) -> None: """ Populates :data:`chess.pgn.Game.errors` with encountered errors and @@ -1277,6 +1291,7 @@ def handle_error(self, error: Exception) -> None: LOGGER.error("%s while parsing %r", error, self.game) self.game.errors.append(error) + @override def result(self) -> GameT: """ Returns the visited :class:`~chess.pgn.Game()`. @@ -1290,20 +1305,24 @@ class HeadersBuilder(BaseVisitor[HeadersT]): @typing.overload def __init__(self: HeadersBuilder[Headers]) -> None: ... @typing.overload - def __init__(self: HeadersBuilder[HeadersT], *, Headers: Type[Headers]) -> None: ... + def __init__(self, *, Headers: Type[HeadersT]) -> None: ... def __init__(self, *, Headers: Any = Headers) -> None: self.Headers = Headers + @override def begin_headers(self) -> HeadersT: self.headers: HeadersT = self.Headers({}) return self.headers + @override def visit_header(self, tagname: str, tagvalue: str) -> None: self.headers[tagname] = tagvalue + @override def end_headers(self) -> SkipType: return SKIP + @override def result(self) -> HeadersT: return self.headers @@ -1314,20 +1333,25 @@ class BoardBuilder(BaseVisitor[chess.Board]): on the move stack. """ + @override def begin_game(self) -> None: self.skip_variation_depth = 0 + @override def begin_variation(self) -> SkipType: self.skip_variation_depth += 1 return SKIP + @override def end_variation(self) -> None: self.skip_variation_depth = max(self.skip_variation_depth - 1, 0) + @override def visit_board(self, board: chess.Board) -> None: if not self.skip_variation_depth: self.board = board + @override def result(self) -> chess.Board: return self.board @@ -1335,15 +1359,19 @@ def result(self) -> chess.Board: class SkipVisitor(BaseVisitor[Literal[True]]): """Skips a game.""" + @override def begin_game(self) -> SkipType: return SKIP + @override def end_headers(self) -> SkipType: return SKIP + @override def begin_variation(self) -> SkipType: return SKIP + @override def result(self) -> Literal[True]: return True @@ -1458,6 +1486,7 @@ class StringExporter(StringExporterMixin, BaseVisitor[str]): There will be no newline characters at the end of the string. """ + @override def result(self) -> str: if self.current_line: return "\n".join(itertools.chain(self.lines, [self.current_line.rstrip()])).rstrip() @@ -1489,6 +1518,7 @@ def __init__(self, handle: TextIO, *, columns: Optional[int] = 80, headers: bool super().__init__(columns=columns, headers=headers, comments=comments, variations=variations) self.handle = handle + @override def begin_game(self) -> None: self.written: int = 0 super().begin_game() @@ -1504,6 +1534,7 @@ def write_line(self, line: str = "") -> None: self.written += self.handle.write(line.rstrip()) self.written += self.handle.write("\n") + @override def result(self) -> int: return self.written From ef13fdbfdb03a10e520a834b32e8a6cac988323a Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sun, 13 Oct 2024 12:44:42 +0200 Subject: [PATCH 077/116] Run pyright in CI --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 576d8b491..ffe0c4bc5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,7 +35,7 @@ jobs: - run: python examples/perft/perft.py -t 1 --variant horde examples/perft/horde.perft - run: python examples/perft/perft.py -t 1 --variant crazyhouse examples/perft/crazyhouse.perft - run: python examples/perft/perft.py -t 1 --variant 3check examples/perft/3check.perft - mypy: + typing: strategy: matrix: os: [ubuntu-latest, windows-latest] @@ -50,6 +50,9 @@ jobs: - run: pip install mypy - run: python -m mypy --strict chess - run: python -m mypy --strict examples/**/*.py + - run: pip install pyright + - run: python -m pyright chess + - run: python -m pyright --strict examples/**/*.py readme: runs-on: ubuntu-latest steps: From 100b1c8b21846c0a9db2f237621368e02294da5f Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sun, 13 Oct 2024 12:45:44 +0200 Subject: [PATCH 078/116] Fixup pyright usage in CI --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ffe0c4bc5..035b7f3d1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -52,7 +52,7 @@ jobs: - run: python -m mypy --strict examples/**/*.py - run: pip install pyright - run: python -m pyright chess - - run: python -m pyright --strict examples/**/*.py + - run: python -m pyright examples/**/*.py readme: runs-on: ubuntu-latest steps: From 4549a1dd5d58661c89ea7a0fd33ae2a3d8e308d8 Mon Sep 17 00:00:00 2001 From: Alexander Lyashuk Date: Sat, 26 Oct 2024 11:36:00 +0200 Subject: [PATCH 079/116] Add 'movesleft' parameter to UCI info parsing --- chess/engine.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chess/engine.py b/chess/engine.py index 3f1e0e399..b979b278f 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -1771,7 +1771,8 @@ def _parse_uci_info(arg: str, root_board: chess.Board, selector: Info = INFO_ALL if parameter == "string": info["string"] = remaining_line break - elif parameter in ["depth", "seldepth", "nodes", "multipv", "currmovenumber", "hashfull", "nps", "tbhits", "cpuload"]: + elif parameter in ["depth", "seldepth", "nodes", "multipv", "currmovenumber", + "hashfull", "nps", "tbhits", "cpuload", "movesleft"]: try: number, remaining_line = _next_token(remaining_line) info[parameter] = int(number) # type: ignore From f1676d51191dbc36cc7d03b4de4da34526e99f88 Mon Sep 17 00:00:00 2001 From: Alexander Lyashuk Date: Sat, 26 Oct 2024 11:41:50 +0200 Subject: [PATCH 080/116] Added a test. --- test.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test.py b/test.py index 68904a50e..6db84d254 100755 --- a/test.py +++ b/test.py @@ -3508,14 +3508,15 @@ def test_uci_info(self): self.assertEqual(info["seldepth"], 8) self.assertEqual(info["score"], chess.engine.PovScore(chess.engine.Mate(+3), chess.WHITE)) - # Info: tbhits, cpuload, hashfull, time, nodes, nps. - info = chess.engine._parse_uci_info("tbhits 123 cpuload 456 hashfull 789 time 987 nodes 654 nps 321", board) + # Info: tbhits, cpuload, hashfull, time, nodes, nps, movesleft. + info = chess.engine._parse_uci_info("tbhits 123 cpuload 456 hashfull 789 movesleft 42 time 987 nodes 654 nps 321", board) self.assertEqual(info["tbhits"], 123) self.assertEqual(info["cpuload"], 456) self.assertEqual(info["hashfull"], 789) self.assertEqual(info["time"], 0.987) self.assertEqual(info["nodes"], 654) self.assertEqual(info["nps"], 321) + self.assertEqual(info["movesleft"], 42) # Hakkapeliitta double spaces. info = chess.engine._parse_uci_info("depth 10 seldepth 9 score cp 22 time 17 nodes 48299 nps 2683000 tbhits 0", board) From 518d662e5467c13630cfe98f9b99e720775d80b1 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 9 Nov 2024 13:20:59 +0100 Subject: [PATCH 081/116] Accept a1a1 as UCI null move (used by lc0, closes #1119) --- chess/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chess/__init__.py b/chess/__init__.py index 4f5791f7c..fdd52ed07 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -705,7 +705,7 @@ def from_uci(cls, uci: str) -> Move: promotion = PIECE_SYMBOLS.index(uci[4]) if len(uci) == 5 else None except ValueError: raise InvalidMoveError(f"invalid uci: {uci!r}") - if from_square == to_square: + if from_square == to_square and from_square != A1: raise InvalidMoveError(f"invalid uci (use 0000 for null moves): {uci!r}") return cls(from_square, to_square, promotion=promotion) else: From 78c765b4dd1741df3a569c3cd96a079d06298823 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 20 Dec 2024 20:25:50 +0100 Subject: [PATCH 082/116] Simplify portable os.O_BINARY access --- chess/polyglot.py | 2 +- chess/syzygy.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chess/polyglot.py b/chess/polyglot.py index 44a68caa5..a7d6807c4 100644 --- a/chess/polyglot.py +++ b/chess/polyglot.py @@ -324,7 +324,7 @@ class MemoryMappedReader: """Maps a Polyglot opening book to memory.""" def __init__(self, filename: StrOrBytesPath) -> None: - fd = os.open(filename, os.O_RDONLY | os.O_BINARY if hasattr(os, "O_BINARY") else os.O_RDONLY) + fd = os.open(filename, os.O_RDONLY | getattr(os, "O_BINARY", 0)) try: self.mmap: Union[mmap.mmap, _EmptyMmap] = mmap.mmap(fd, 0, access=mmap.ACCESS_READ) except (ValueError, OSError): diff --git a/chess/syzygy.py b/chess/syzygy.py index 89f8c2596..5ead9922c 100644 --- a/chess/syzygy.py +++ b/chess/syzygy.py @@ -587,7 +587,7 @@ def __init__(self, path: str, *, variant: Type[chess.Board] = chess.Board) -> No def init_mmap(self) -> None: if self.data is None: - fd = os.open(self.path, os.O_RDONLY | os.O_BINARY if hasattr(os, "O_BINARY") else os.O_RDONLY) + fd = os.open(self.path, os.O_RDONLY | getattr(os, "O_BINARY", 0)) try: data = mmap.mmap(fd, 0, access=mmap.ACCESS_READ) finally: From ea87e07a22e65bc3b541f2beaefd83e4477d20d1 Mon Sep 17 00:00:00 2001 From: Manan Date: Tue, 31 Dec 2024 18:09:35 +0530 Subject: [PATCH 083/116] Update __init__.py Removed redundant `Generic` import from `chess/__init__.py` --- chess/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chess/__init__.py b/chess/__init__.py index b01a2e478..8d0a68258 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -22,7 +22,7 @@ import itertools import typing -from typing import ClassVar, Callable, Counter, Dict, Generic, Hashable, Iterable, Iterator, List, Literal, Mapping, Optional, SupportsInt, Tuple, Type, TypeVar, Union +from typing import ClassVar, Callable, Counter, Dict, Hashable, Iterable, Iterator, List, Literal, Mapping, Optional, SupportsInt, Tuple, Type, TypeVar, Union if typing.TYPE_CHECKING: from typing_extensions import Self, TypeAlias From 5f76cb296a10a81672cbf60368a62e2c85adedec Mon Sep 17 00:00:00 2001 From: Adrien Barbaresi Date: Sun, 5 Jan 2025 17:42:33 +0100 Subject: [PATCH 084/116] gaviota.py: simplify and improve code --- chess/gaviota.py | 553 +++++++++++++++-------------------------------- 1 file changed, 179 insertions(+), 374 deletions(-) diff --git a/chess/gaviota.py b/chess/gaviota.py index 39173b593..8cfd40d1e 100644 --- a/chess/gaviota.py +++ b/chess/gaviota.py @@ -11,11 +11,12 @@ import struct import typing -import chess - +from itertools import accumulate from types import TracebackType from typing import BinaryIO, Callable, Dict, List, Optional, Tuple, Type, Union +import chess + LOGGER = logging.getLogger(__name__) @@ -109,30 +110,19 @@ def idx_is_empty(x: int) -> int: def flip_type(x: chess.Square, y: chess.Square) -> int: ret = 0 + file_x, rank_x = chess.square_file(x), chess.square_rank(x) + file_y, rank_y = chess.square_file(y), chess.square_rank(y) - if chess.square_file(x) > 3: - x = flip_we(x) - y = flip_we(y) + if file_x > 3: + x, y = flip_we(x), flip_we(y) ret |= 1 - if chess.square_rank(x) > 3: - x = flip_ns(x) - y = flip_ns(y) + if rank_x > 3: + x, y = flip_ns(x), flip_ns(y) ret |= 2 - rowx = chess.square_rank(x) - colx = chess.square_file(x) - - if rowx > colx: - x = flip_nw_se(x) - y = flip_nw_se(y) - ret |= 4 - - rowy = chess.square_rank(y) - coly = chess.square_file(y) - if rowx == colx and rowy > coly: - x = flip_nw_se(x) - y = flip_nw_se(y) + if (rank_x, file_x) > (rank_y, file_y): + x, y = flip_nw_se(x), flip_nw_se(y) ret |= 4 return ret @@ -145,7 +135,6 @@ def init_flipt() -> List[List[int]]: def init_pp48_idx() -> Tuple[List[List[int]], List[int], List[int]]: MAX_I = MAX_J = 48 - idx = 0 pp48_idx = [[-1] * MAX_J for _ in range(MAX_I)] pp48_sq_x = [NOSQUARE] * MAX_PP48_INDEX pp48_sq_y = [NOSQUARE] * MAX_PP48_INDEX @@ -157,10 +146,8 @@ def init_pp48_idx() -> Tuple[List[List[int]], List[int], List[int]]: j = flip_we(flip_ns(b)) - 8 if idx_is_empty(pp48_idx[i][j]): - pp48_idx[i][j] = idx - pp48_idx[j][i] = idx - pp48_sq_x[idx] = i - pp48_sq_y[idx] = j + pp48_idx[i][j] = pp48_idx[j][i] = idx + pp48_sq_x[idx], pp48_sq_y[idx] = i, j idx += 1 return pp48_idx, pp48_sq_x, pp48_sq_y @@ -179,26 +166,15 @@ def init_ppp48_idx() -> Tuple[List[List[List[int]]], List[int], List[int], List[ for x in range(48): for y in range(x + 1, 48): for z in range(y + 1, 48): - a = ITOSQ[x] - b = ITOSQ[y] - c = ITOSQ[z] - if not in_queenside(b) or not in_queenside(c): + if not (in_queenside(ITOSQ[y]) and in_queenside(ITOSQ[z])): continue - i = a - 8 - j = b - 8 - k = c - 8 + i, j, k = ITOSQ[x] - 8, ITOSQ[y] - 8, ITOSQ[z] - 8 if idx_is_empty(ppp48_idx[i][j][k]): - ppp48_idx[i][j][k] = idx - ppp48_idx[i][k][j] = idx - ppp48_idx[j][i][k] = idx - ppp48_idx[j][k][i] = idx - ppp48_idx[k][i][j] = idx - ppp48_idx[k][j][i] = idx - ppp48_sq_x[idx] = i - ppp48_sq_y[idx] = j - ppp48_sq_z[idx] = k + for p in [(i, j, k), (i, k, j), (j, i, k), (j, k, i), (k, i, j), (k, j, i)]: + ppp48_idx[p[0]][p[1]][p[2]] = idx + ppp48_sq_x[idx], ppp48_sq_y[idx], ppp48_sq_z[idx] = i, j, k idx += 1 return ppp48_idx, ppp48_sq_x, ppp48_sq_y, ppp48_sq_z @@ -216,8 +192,7 @@ def init_aaidx() -> Tuple[List[int], List[List[int]]]: if idx_is_empty(aaidx[x][y]): # Still empty. - aaidx[x][y] = idx - aaidx[y][x] = idx + aaidx[x][y] = aaidx[y][x] = idx aabase[idx] = x idx += 1 @@ -229,24 +204,10 @@ def init_aaidx() -> Tuple[List[int], List[List[int]]]: def init_aaa() -> Tuple[List[int], List[List[int]]]: # Get aaa_base. comb = [a * (a - 1) // 2 for a in range(64)] - - accum = 0 - aaa_base = [0] * 64 - for a in range(64 - 1): - accum += comb[a] - aaa_base[a + 1] = accum + aaa_base = list(accumulate(comb)) # Get aaa_xyz. - aaa_xyz = [[-1] * 3 for _ in range(MAX_AAAINDEX)] - - idx = 0 - for z in range(64): - for y in range(z): - for x in range(y): - aaa_xyz[idx][0] = x - aaa_xyz[idx][1] = y - aaa_xyz[idx][2] = z - idx += 1 + aaa_xyz = [[x, y, z] for z in range(64) for y in range(z) for x in range(y)] return aaa_base, aaa_xyz @@ -351,34 +312,33 @@ def init_ppidx() -> Tuple[List[List[int]], List[int], List[int]]: def norm_kkindex(x: chess.Square, y: chess.Square) -> Tuple[int, int]: - if chess.square_file(x) > 3: - x = flip_we(x) - y = flip_we(y) + file_x, rank_x = chess.square_file(x), chess.square_rank(x) + + if file_x > 3: + x, y = flip_we(x), flip_we(y) - if chess.square_rank(x) > 3: - x = flip_ns(x) - y = flip_ns(y) + if rank_x > 3: + x, y = flip_ns(x), flip_ns(y) rowx = chess.square_rank(x) colx = chess.square_file(x) if rowx > colx: - x = flip_nw_se(x) - y = flip_nw_se(y) + x, y = flip_nw_se(x), flip_nw_se(y) rowy = chess.square_rank(y) coly = chess.square_file(y) if rowx == colx and rowy > coly: - x = flip_nw_se(x) - y = flip_nw_se(y) + x, y = flip_nw_se(x), flip_nw_se(y) return x, y + def init_kkidx() -> Tuple[List[List[int]], List[int], List[int]]: kkidx = [[-1] * 64 for _ in range(64)] - bksq = [-1] * MAX_KKINDEX - wksq = [-1] * MAX_KKINDEX + bksq, wksq = [-1] * MAX_KKINDEX, [-1] * MAX_KKINDEX + idx = 0 for x in range(64): for y in range(64): @@ -388,10 +348,8 @@ def init_kkidx() -> Tuple[List[List[int]], List[int], List[int]]: i, j = norm_kkindex(x, y) if idx_is_empty(kkidx[i][j]): - kkidx[i][j] = idx - kkidx[x][y] = idx - bksq[idx] = i - wksq[idx] = j + kkidx[i][j] = kkidx[x][y] = idx + bksq[idx], wksq[idx] = i, j idx += 1 return kkidx, wksq, bksq @@ -404,20 +362,12 @@ def kxk_pctoindex(c: Request) -> int: ft = flip_type(c.black_piece_squares[0], c.white_piece_squares[0]) - ws = c.white_piece_squares - bs = c.black_piece_squares - - if (ft & 1) != 0: - ws = [flip_we(b) for b in ws] - bs = [flip_we(b) for b in bs] - - if (ft & 2) != 0: - ws = [flip_ns(b) for b in ws] - bs = [flip_ns(b) for b in bs] + ws, bs = c.white_piece_squares, c.black_piece_squares - if (ft & 4) != 0: - ws = [flip_nw_se(b) for b in ws] - bs = [flip_nw_se(b) for b in bs] + for f, flip in [(1, flip_we), (2, flip_ns), (4, flip_nw_se)]: + if ft & f: + ws = [flip(b) for b in ws] + bs = [flip(b) for b in bs] ki = KKIDX[bs[0]][ws[0]] # KKIDX[black king][white king] @@ -426,6 +376,14 @@ def kxk_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ws[1] + +def flip_we_col(*pieces: int) -> tuple[int, ...]: + if (pieces[0] & 7) > 3: + # Column is more than 3, i.e., e, f, g or h. + pieces = tuple(flip_we(piece) for piece in pieces) + return pieces + + def kapkb_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 * 64 * 64 BLOCK_B = 64 * 64 * 64 @@ -438,24 +396,17 @@ def kapkb_pctoindex(c: Request) -> int: bk = c.black_piece_squares[0] ba = c.black_piece_squares[1] - if not (chess.A2 <= pawn < chess.A8): + if not chess.A2 <= pawn < chess.A8: return NOINDEX - if (pawn & 7) > 3: - # Column is more than 3, i.e., e, f, g or h. - pawn = flip_we(pawn) - wk = flip_we(wk) - bk = flip_we(bk) - wa = flip_we(wa) - ba = flip_we(ba) + pawn, wk, bk, wa, ba = flip_we_col(pawn, wk, bk, wa, ba) - sq = pawn - sq ^= 56 # flip_ns - sq -= 8 # Down one row - pslice = (sq + (sq & 3)) >> 1 + # flip_ns, down one row + pslice = ((pawn ^ 56) - 8 + (pawn & 3)) >> 1 return pslice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + wa * BLOCK_D + ba + def kabpk_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 * 64 * 64 BLOCK_B = 64 * 64 * 64 @@ -468,18 +419,13 @@ def kabpk_pctoindex(c: Request) -> int: pawn = c.white_piece_squares[3] bk = c.black_piece_squares[0] - if (pawn & 7) > 3: - # Column is more than 3, i.e., e, f, g or h. - pawn = flip_we(pawn) - wk = flip_we(wk) - bk = flip_we(bk) - wa = flip_we(wa) - wb = flip_we(wb) + pawn, wk, bk, wa, ba = flip_we_col(pawn, wk, bk, wa, ba) pslice = wsq_to_pidx24(pawn) return pslice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + wa * BLOCK_D + wb + def kabkp_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 * 64 * 64 BLOCK_B = 64 * 64 * 64 @@ -492,23 +438,17 @@ def kabkp_pctoindex(c: Request) -> int: bk = c.black_piece_squares[0] wb = c.white_piece_squares[2] - if not (chess.A2 <= pawn < chess.A8): + if not chess.A2 <= pawn < chess.A8: return NOINDEX - if (pawn & 7) > 3: - # Column is more than 3, i.e., e, f, g or h. - pawn = flip_we(pawn) - wk = flip_we(wk) - bk = flip_we(bk) - wa = flip_we(wa) - wb = flip_we(wb) + pawn, wk, bk, wa, ba = flip_we_col(pawn, wk, bk, wa, ba) - sq = pawn - sq -= 8 # Down one row - pslice = (sq + (sq & 3)) >> 1 + # Down one row + pslice = ((pawn - 8) + (pawn & 3)) >> 1 return pslice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + wa * BLOCK_D + wb + def kaapk_pctoindex(c: Request) -> int: BLOCK_C = MAX_AAINDEX BLOCK_B = 64 * BLOCK_C @@ -520,13 +460,7 @@ def kaapk_pctoindex(c: Request) -> int: pawn = c.white_piece_squares[3] bk = c.black_piece_squares[0] - if (pawn & 7) > 3: - # Column is more than 3, i.e., e, f, g or h. - pawn = flip_we(pawn) - wk = flip_we(wk) - bk = flip_we(bk) - wa = flip_we(wa) - wa2 = flip_we(wa2) + pawn, wk, bk, wa, ba = flip_we_col(pawn, wk, bk, wa, ba) pslice = wsq_to_pidx24(pawn) @@ -537,6 +471,7 @@ def kaapk_pctoindex(c: Request) -> int: return pslice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + aa_combo + def kaakp_pctoindex(c: Request) -> int: BLOCK_C = MAX_AAINDEX BLOCK_B = 64 * BLOCK_C @@ -548,13 +483,7 @@ def kaakp_pctoindex(c: Request) -> int: bk = c.black_piece_squares[0] pawn = c.black_piece_squares[1] - if (pawn & 7) > 3: - # Column is more than 3, i.e., e, f, g or h. - pawn = flip_we(pawn) - wk = flip_we(wk) - bk = flip_we(bk) - wa = flip_we(wa) - wa2 = flip_we(wa2) + pawn, wk, bk, wa, ba = flip_we_col(pawn, wk, bk, wa, ba) pawn = flip_ns(pawn) pslice = wsq_to_pidx24(pawn) @@ -566,6 +495,7 @@ def kaakp_pctoindex(c: Request) -> int: return pslice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + aa_combo + def kapkp_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 * 64 BLOCK_B = 64 * 64 @@ -573,30 +503,20 @@ def kapkp_pctoindex(c: Request) -> int: wk = c.white_piece_squares[0] wa = c.white_piece_squares[1] - pawn_a = c.white_piece_squares[2] + anchor = c.white_piece_squares[2] bk = c.black_piece_squares[0] - pawn_b = c.black_piece_squares[1] - - anchor = pawn_a - loosen = pawn_b + loosen = c.black_piece_squares[1] - if (anchor & 7) > 3: - # Column is more than 3, i.e., e, f, g or h. - anchor = flip_we(anchor) - loosen = flip_we(loosen) - wk = flip_we(wk) - bk = flip_we(bk) - wa = flip_we(wa) + anchor, loosen, wk, bk, wa = flip_we_col(anchor, loosen, wk, bk, wa) - m = wsq_to_pidx24(anchor) - n = loosen - 8 - pp_slice = m * 48 + n + pp_slice = wsq_to_pidx24(anchor) * 48 + loosen - 8 if idx_is_empty(pp_slice): return NOINDEX return pp_slice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + wa + def kappk_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 * 64 BLOCK_B = 64 * 64 @@ -610,13 +530,7 @@ def kappk_pctoindex(c: Request) -> int: anchor, loosen = pp_putanchorfirst(pawn_a, pawn_b) - if (anchor & 7) > 3: - # Column is more than 3, i.e., e, f, g or h. - anchor = flip_we(anchor) - loosen = flip_we(loosen) - wk = flip_we(wk) - bk = flip_we(bk) - wa = flip_we(wa) + anchor, loosen, wk, bk, wa = flip_we_col(anchor, loosen, wk, bk, wa) i = wsq_to_pidx24(anchor) j = wsq_to_pidx48(loosen) @@ -628,6 +542,7 @@ def kappk_pctoindex(c: Request) -> int: return pp_slice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + wa + def kppka_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 * 64 BLOCK_B = 64 * 64 @@ -641,12 +556,7 @@ def kppka_pctoindex(c: Request) -> int: anchor, loosen = pp_putanchorfirst(pawn_a, pawn_b) - if (anchor & 7) > 3: - anchor = flip_we(anchor) - loosen = flip_we(loosen) - wk = flip_we(wk) - bk = flip_we(bk) - ba = flip_we(ba) + anchor, loosen, wk, bk, wa = flip_we_col(anchor, loosen, wk, bk, wa) i = wsq_to_pidx24(anchor) j = wsq_to_pidx48(loosen) @@ -658,6 +568,7 @@ def kppka_pctoindex(c: Request) -> int: return pp_slice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + ba + def kabck_pctoindex(c: Request) -> int: N_WHITE = 4 N_BLACK = 1 @@ -689,6 +600,7 @@ def kabck_pctoindex(c: Request) -> int: return ki * BLOCK_A + ws[1] * BLOCK_B + ws[2] * BLOCK_C + ws[3] + def kabbk_pctoindex(c: Request) -> int: N_WHITE = 4 N_BLACK = 1 @@ -720,6 +632,7 @@ def kabbk_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ai * BLOCK_Bx + ws[1] + def kaabk_pctoindex(c: Request) -> int: N_WHITE = 4 N_BLACK = 1 @@ -751,45 +664,23 @@ def kaabk_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ai * BLOCK_Bx + ws[3] + def aaa_getsubi(x: int, y: int, z: int) -> int: - bse = AAA_BASE[z] - calc_idx = x + (y - 1) * y // 2 + bse - return calc_idx + return x + (y - 1) * y // 2 + AAA_BASE[z] + def kaaak_pctoindex(c: Request) -> int: - N_WHITE = 4 - N_BLACK = 1 BLOCK_Ax = MAX_AAAINDEX - - ws = c.white_piece_squares[:N_WHITE] - bs = c.black_piece_squares[:N_BLACK] - + ws = c.white_piece_squares[:4] + bs = c.black_piece_squares[:1] ft = FLIPT[c.black_piece_squares[0]][c.white_piece_squares[0]] - if (ft & WE_FLAG) != 0: - ws = [flip_we(i) for i in ws] - bs = [flip_we(i) for i in bs] - - if (ft & NS_FLAG) != 0: - ws = [flip_ns(i) for i in ws] - bs = [flip_ns(i) for i in bs] + for flag, flip in [(WE_FLAG, flip_we), (NS_FLAG, flip_ns), (NW_SE_FLAG, flip_nw_se)]: + if ft & flag: + ws = [flip(i) for i in ws] + bs = [flip(i) for i in bs] - if (ft & NW_SE_FLAG) != 0: - ws = [flip_nw_se(i) for i in ws] - bs = [flip_nw_se(i) for i in bs] - - if ws[2] < ws[1]: - tmp = ws[1] - ws[1] = ws[2] - ws[2] = tmp - if ws[3] < ws[2]: - tmp = ws[2] - ws[2] = ws[3] - ws[3] = tmp - if ws[2] < ws[1]: - tmp = ws[1] - ws[1] = ws[2] - ws[2] = tmp + ws[1:4] = sorted(ws[1:4]) ki = KKIDX[bs[0]][ws[0]] @@ -803,6 +694,7 @@ def kaaak_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ai + def kppkp_pctoindex(c: Request) -> int: BLOCK_Ax = MAX_PP48_INDEX * 64 * 64 BLOCK_Bx = 64 * 64 @@ -814,12 +706,7 @@ def kppkp_pctoindex(c: Request) -> int: bk = c.black_piece_squares[0] pawn_c = c.black_piece_squares[1] - if (pawn_c & 7) > 3: - wk = flip_we(wk) - pawn_a = flip_we(pawn_a) - pawn_b = flip_we(pawn_b) - bk = flip_we(bk) - pawn_c = flip_we(pawn_c) + pawn_c, wk, pawn_a, pawn_b, bk = flip_we_col(pawn_c, wk, pawn_a, pawn_b, bk) i = flip_we(flip_ns(pawn_a)) - 8 j = flip_we(flip_ns(pawn_b)) - 8 @@ -834,28 +721,19 @@ def kppkp_pctoindex(c: Request) -> int: return k * BLOCK_Ax + pp48_slice * BLOCK_Bx + wk * BLOCK_Cx + bk + def kaakb_pctoindex(c: Request) -> int: - N_WHITE = 3 - N_BLACK = 2 BLOCK_Bx = 64 BLOCK_Ax = BLOCK_Bx * MAX_AAINDEX + ws = c.white_piece_squares[:3] + bs = c.black_piece_squares[:2] ft = FLIPT[c.black_piece_squares[0]][c.white_piece_squares[0]] - ws = c.white_piece_squares[:N_WHITE] - bs = c.black_piece_squares[:N_BLACK] - - if (ft & WE_FLAG) != 0: - ws = [flip_we(i) for i in ws] - bs = [flip_we(i) for i in bs] - - if (ft & NS_FLAG) != 0: - ws = [flip_ns(i) for i in ws] - bs = [flip_ns(i) for i in bs] - - if (ft & NW_SE_FLAG) != 0: - ws = [flip_nw_se(i) for i in ws] - bs = [flip_nw_se(i) for i in bs] + for flag, flip in [(WE_FLAG, flip_we), (NS_FLAG, flip_ns), (NW_SE_FLAG, flip_nw_se)]: + if ft & flag: + ws = [flip(i) for i in ws] + bs = [flip(i) for i in bs] ki = KKIDX[bs[0]][ws[0]] # KKIDX[black king][white king] ai = AAIDX[ws[1]][ws[2]] @@ -865,6 +743,7 @@ def kaakb_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ai * BLOCK_Bx + bs[1] + def kabkc_pctoindex(c: Request) -> int: N_WHITE = 3 N_BLACK = 2 @@ -878,17 +757,10 @@ def kabkc_pctoindex(c: Request) -> int: ws = c.white_piece_squares[:N_WHITE] bs = c.black_piece_squares[:N_BLACK] - if (ft & WE_FLAG) != 0: - ws = [flip_we(i) for i in ws] - bs = [flip_we(i) for i in bs] - - if (ft & NS_FLAG) != 0: - ws = [flip_ns(i) for i in ws] - bs = [flip_ns(i) for i in bs] - - if (ft & NW_SE_FLAG) != 0: - ws = [flip_nw_se(i) for i in ws] - bs = [flip_nw_se(i) for i in bs] + for flag, flip in [(WE_FLAG, flip_we), (NS_FLAG, flip_ns), (NW_SE_FLAG, flip_nw_se)]: + if ft & flag: + ws = [flip(i) for i in ws] + bs = [flip(i) for i in bs] ki = KKIDX[bs[0]][ws[0]] # KKIDX [black king] [white king] @@ -897,23 +769,17 @@ def kabkc_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ws[1] * BLOCK_Bx + ws[2] * BLOCK_Cx + bs[1] + def kpkp_pctoindex(c: Request) -> int: BLOCK_Ax = 64 * 64 BLOCK_Bx = 64 wk = c.white_piece_squares[0] bk = c.black_piece_squares[0] - pawn_a = c.white_piece_squares[1] - pawn_b = c.black_piece_squares[1] + anchor = c.white_piece_squares[1] + loosen = c.black_piece_squares[1] - anchor = pawn_a - loosen = pawn_b - - if (anchor & 7) > 3: - anchor = flip_we(anchor) - loosen = flip_we(loosen) - wk = flip_we(wk) - bk = flip_we(bk) + anchor, loosen, wk, bk = flip_we_col(anchor, loosen, wk, bk) m = wsq_to_pidx24(anchor) n = loosen - 8 @@ -925,6 +791,7 @@ def kpkp_pctoindex(c: Request) -> int: return pp_slice * BLOCK_Ax + wk * BLOCK_Bx + bk + def kppk_pctoindex(c: Request) -> int: BLOCK_Ax = 64 * 64 BLOCK_Bx = 64 @@ -935,11 +802,7 @@ def kppk_pctoindex(c: Request) -> int: anchor, loosen = pp_putanchorfirst(pawn_a, pawn_b) - if (anchor & 7) > 3: - anchor = flip_we(anchor) - loosen = flip_we(loosen) - wk = flip_we(wk) - bk = flip_we(bk) + anchor, loosen, wk, bk = flip_we_col(anchor, loosen, wk, bk) i = wsq_to_pidx24(anchor) j = wsq_to_pidx48(loosen) @@ -951,6 +814,7 @@ def kppk_pctoindex(c: Request) -> int: return pp_slice * BLOCK_Ax + wk * BLOCK_Bx + bk + def kapk_pctoindex(c: Request) -> int: BLOCK_Ax = 64 * 64 * 64 BLOCK_Bx = 64 * 64 @@ -961,22 +825,17 @@ def kapk_pctoindex(c: Request) -> int: wk = c.white_piece_squares[0] bk = c.black_piece_squares[0] - if not (chess.A2 <= pawn < chess.A8): + if not chess.A2 <= pawn < chess.A8: return NOINDEX - if (pawn & 7) > 3: - pawn = flip_we(pawn) - wk = flip_we(wk) - bk = flip_we(bk) - wa = flip_we(wa) + pawn, wk, bk, wa = flip_we_col(pawn, wk, bk, wa) - sq = pawn - sq ^= 56 # flip_ns - sq -= 8 # Down one row + sq = pawn ^ 56 - 8 # flip_ns and down one row pslice = ((sq + (sq & 3)) >> 1) return pslice * BLOCK_Ax + wk * BLOCK_Bx + bk * BLOCK_Cx + wa + def kabk_pctoindex(c: Request) -> int: BLOCK_Ax = 64 * 64 BLOCK_Bx = 64 @@ -986,17 +845,10 @@ def kabk_pctoindex(c: Request) -> int: ws = c.white_piece_squares bs = c.black_piece_squares - if (ft & 1) != 0: - ws = [flip_we(b) for b in ws] - bs = [flip_we(b) for b in bs] - - if (ft & 2) != 0: - ws = [flip_ns(b) for b in ws] - bs = [flip_ns(b) for b in bs] - - if (ft & 4) != 0: - ws = [flip_nw_se(b) for b in ws] - bs = [flip_nw_se(b) for b in bs] + for flag, flip in [(1, flip_we), (2, flip_ns), (4, flip_nw_se)]: + if ft & flag: + ws = [flip(b) for b in ws] + bs = [flip(b) for b in bs] ki = KKIDX[bs[0]][ws[0]] # KKIDX[black king][white king] @@ -1005,6 +857,7 @@ def kabk_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ws[1] * BLOCK_Bx + ws[2] + def kakp_pctoindex(c: Request) -> int: BLOCK_Ax = 64 * 64 * 64 BLOCK_Bx = 64 * 64 @@ -1015,42 +868,29 @@ def kakp_pctoindex(c: Request) -> int: wk = c.white_piece_squares[0] bk = c.black_piece_squares[0] - if not (chess.A2 <= pawn < chess.A8): + if not chess.A2 <= pawn < chess.A8: return NOINDEX - if (pawn & 7) > 3: - pawn = flip_we(pawn) - wk = flip_we(wk) - bk = flip_we(bk) - wa = flip_we(wa) + pawn, wk, bk, wa = flip_we_col(pawn, wk, bk, wa) - sq = pawn - sq -= 8 # Down one row - pslice = (sq + (sq & 3)) >> 1 + # Down one row + pslice = ((pawn - 8) + ((pawn - 8) & 3)) >> 1 return pslice * BLOCK_Ax + wk * BLOCK_Bx + bk * BLOCK_Cx + wa + def kaak_pctoindex(c: Request) -> int: - N_WHITE = 3 - N_BLACK = 1 BLOCK_Ax = MAX_AAINDEX ft = FLIPT[c.black_piece_squares[0]][c.white_piece_squares[0]] - ws = c.white_piece_squares[:N_WHITE] - bs = c.black_piece_squares[:N_BLACK] - - if (ft & WE_FLAG) != 0: - ws = [flip_we(i) for i in ws] - bs = [flip_we(i) for i in bs] + ws = c.white_piece_squares[:3] + bs = c.black_piece_squares[:1] - if (ft & NS_FLAG) != 0: - ws = [flip_ns(i) for i in ws] - bs = [flip_ns(i) for i in bs] - - if (ft & NW_SE_FLAG) != 0: - ws = [flip_nw_se(i) for i in ws] - bs = [flip_nw_se(i) for i in bs] + for flag, flip in [(WE_FLAG, flip_we), (NS_FLAG, flip_ns), (NW_SE_FLAG, flip_nw_se)]: + if ft & flag: + ws = [flip(i) for i in ws] + bs = [flip(i) for i in bs] ki = KKIDX[bs[0]][ws[0]] # KKIDX[black king][white king] ai = AAIDX[ws[1]][ws[2]] @@ -1060,6 +900,7 @@ def kaak_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ai + def kakb_pctoindex(c: Request) -> int: BLOCK_Ax = 64 * 64 BLOCK_Bx = 64 @@ -1069,23 +910,9 @@ def kakb_pctoindex(c: Request) -> int: ws = c.white_piece_squares[:] bs = c.black_piece_squares[:] - if (ft & 1) != 0: - ws[0] = flip_we(ws[0]) - ws[1] = flip_we(ws[1]) - bs[0] = flip_we(bs[0]) - bs[1] = flip_we(bs[1]) - - if (ft & 2) != 0: - ws[0] = flip_ns(ws[0]) - ws[1] = flip_ns(ws[1]) - bs[0] = flip_ns(bs[0]) - bs[1] = flip_ns(bs[1]) - - if (ft & 4) != 0: - ws[0] = flip_nw_se(ws[0]) - ws[1] = flip_nw_se(ws[1]) - bs[0] = flip_nw_se(bs[0]) - bs[1] = flip_nw_se(bs[1]) + for bit, flip in [(1, flip_we), (2, flip_ns), (4, flip_nw_se)]: + if ft & bit: + ws[0], ws[1], bs[0], bs[1] = flip(ws[0]), flip(ws[1]), flip(bs[0]), flip(bs[1]) ki = KKIDX[bs[0]][ws[0]] # KKIDX[black king][white king] @@ -1094,6 +921,7 @@ def kakb_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ws[1] * BLOCK_Bx + bs[1] + def kpk_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 BLOCK_B = 64 @@ -1102,21 +930,16 @@ def kpk_pctoindex(c: Request) -> int: wk = c.white_piece_squares[0] bk = c.black_piece_squares[0] - if not (chess.A2 <= pawn < chess.A8): + if not chess.A2 <= pawn < chess.A8: return NOINDEX - if (pawn & 7) > 3: - pawn = flip_we(pawn) - wk = flip_we(wk) - bk = flip_we(bk) + pawn, wk, bk = flip_we_col(pawn, wk, bk) - sq = pawn - sq ^= 56 # flip_ns - sq -= 8 # Down one row + sq = (pawn ^ 56) - 8 # flip_ns, down one row pslice = ((sq + (sq & 3)) >> 1) - res = pslice * BLOCK_A + wk * BLOCK_B + bk - return res + return pslice * BLOCK_A + wk * BLOCK_B + bk + def kpppk_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 @@ -1126,26 +949,16 @@ def kpppk_pctoindex(c: Request) -> int: pawn_a = c.white_piece_squares[1] pawn_b = c.white_piece_squares[2] pawn_c = c.white_piece_squares[3] - bk = c.black_piece_squares[0] - i = pawn_a - 8 - j = pawn_b - 8 - k = pawn_c - 8 - + i, j, k = [x - 8 for x in (pawn_a, pawn_b, pawn_c)] ppp48_slice = PPP48_IDX[i][j][k] if idx_is_empty(ppp48_slice): - wk = flip_we(wk) - pawn_a = flip_we(pawn_a) - pawn_b = flip_we(pawn_b) - pawn_c = flip_we(pawn_c) - bk = flip_we(bk) - - i = pawn_a - 8 - j = pawn_b - 8 - k = pawn_c - 8 + wk, pawn_a, pawn_b, pawn_c, bk = [flip_we(x) for x in (wk, pawn_a, pawn_b, pawn_c, bk)] + # removing this doesn't impact the tests? + i, j, k = [x - 8 for x in (pawn_a, pawn_b, pawn_c)] ppp48_slice = PPP48_IDX[i][j][k] if idx_is_empty(ppp48_slice): @@ -1330,13 +1143,15 @@ def __init__(self, maxindex: int, slice_n: int, pctoi: Callable[[Request], int]) def sortlists(ws: List[int], wp: List[int]) -> Tuple[List[int], List[int]]: - z = sorted(zip(wp, ws), key=lambda x: x[0], reverse=True) + z = sorted(zip(wp, ws), reverse=True) wp2, ws2 = zip(*z) return list(ws2), list(wp2) + def egtb_block_unpack(side: int, n: int, bp: bytes) -> List[int]: return [dtm_unpack(side, i) for i in bp[:n]] + def split_index(i: int) -> Tuple[int, int]: return divmod(i, ENTRIES_PER_BLOCK) @@ -1360,19 +1175,23 @@ def removepiece(ys: List[int], yp: List[int], j: int) -> None: del ys[j] del yp[j] + def opp(side: int) -> int: return 1 if side == 0 else 0 -def adjust_up(dist: int) -> int: - udist = dist - sw = udist & INFOMASK - if sw in [iWMATE, iWMATEt, iBMATE, iBMATEt]: - udist += (1 << PLYSHIFT) +def adjust_up(dist: int) -> int: + if (dist & INFOMASK) in [iWMATE, iWMATEt, iBMATE, iBMATEt]: + return dist + (1 << PLYSHIFT) + return dist - return udist def bestx(side: int, a: int, b: int) -> int: + if a == iFORBID: + return b + if b == iFORBID: + return a + # 0 = selectfirst # 1 = selectlowest # 2 = selecthighest @@ -1385,33 +1204,25 @@ def bestx(side: int, a: int, b: int) -> int: [3, 3, 3, 0], # forbid ] - xorkey = [0, 3] - - if a == iFORBID: - return b - if b == iFORBID: - return a - retu = [a, a, b, b] if b < a: - retu[1] = b - retu[2] = a + retu[1:2] = [b, a] - key = comparison[a & 3][b & 3] ^ xorkey[side] + key = comparison[a & 3][b & 3] ^ [0, 3][side] # ^ xorkey return retu[key] + def unpackdist(d: int) -> Tuple[int, int]: return d >> PLYSHIFT, d & INFOMASK -def dtm_unpack(stm: int, packed: int) -> int: - p = packed - if p in [iDRAW, iFORBID]: - return p +def dtm_unpack(stm: int, packed: int) -> int: + if packed in [iDRAW, iFORBID]: + return packed - info = p & 3 - store = p >> 2 + info = packed & 3 + store = packed >> 2 if stm == 0: if info == iWMATE: @@ -1587,28 +1398,23 @@ def probe_dtm(self, board: chess.Board) -> int: if req.realside == 1: if req.is_reversed: return ply - else: - return -ply - else: - if req.is_reversed: - return -ply - else: - return ply - elif res == iBMATE: + return -ply + if req.is_reversed: + return -ply + return ply + + if res == iBMATE: # Black mates in the stored position. if req.realside == 0: if req.is_reversed: return ply - else: - return -ply - else: - if req.is_reversed: - return -ply - else: - return ply - else: - # Draw. - return 0 + return -ply + if req.is_reversed: + return -ply + return ply + + # Draw. + return 0 def get_dtm(self, board: chess.Board, default: Optional[int] = None) -> Optional[int]: try: @@ -1644,12 +1450,12 @@ def probe_wdl(self, board: chess.Board) -> int: if dtm == 0: if board.is_checkmate(): return -1 - else: - return 0 - elif dtm > 0: + return 0 + + if dtm > 0: return 1 - else: - return -1 + + return -1 def get_wdl(self, board: chess.Board, default: Optional[int] = None) -> Optional[int]: try: @@ -1795,8 +1601,7 @@ def egtb_block_getsize(self, req: Request, idx: int) -> int: if (offset + blocksz) > maxindex: return maxindex - offset # Last block size - else: - return blocksz # Size of a normal block + return blocksz # Size of a normal block def _tb_probe(self, req: Request) -> int: stream = self._setup_tablebase(req) From 4849537943da0cf3aa839770af2fa9f4c1432424 Mon Sep 17 00:00:00 2001 From: Adrien Barbaresi Date: Sun, 5 Jan 2025 17:49:53 +0100 Subject: [PATCH 085/116] fix mypy errors --- chess/gaviota.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/chess/gaviota.py b/chess/gaviota.py index 8cfd40d1e..9abe11bcb 100644 --- a/chess/gaviota.py +++ b/chess/gaviota.py @@ -419,7 +419,7 @@ def kabpk_pctoindex(c: Request) -> int: pawn = c.white_piece_squares[3] bk = c.black_piece_squares[0] - pawn, wk, bk, wa, ba = flip_we_col(pawn, wk, bk, wa, ba) + pawn, wk, bk, wa, wb = flip_we_col(pawn, wk, bk, wa, wb) pslice = wsq_to_pidx24(pawn) @@ -441,7 +441,7 @@ def kabkp_pctoindex(c: Request) -> int: if not chess.A2 <= pawn < chess.A8: return NOINDEX - pawn, wk, bk, wa, ba = flip_we_col(pawn, wk, bk, wa, ba) + pawn, wk, bk, wa, wb = flip_we_col(pawn, wk, bk, wa, wb) # Down one row pslice = ((pawn - 8) + (pawn & 3)) >> 1 @@ -460,7 +460,7 @@ def kaapk_pctoindex(c: Request) -> int: pawn = c.white_piece_squares[3] bk = c.black_piece_squares[0] - pawn, wk, bk, wa, ba = flip_we_col(pawn, wk, bk, wa, ba) + pawn, wk, bk, wa, wa2 = flip_we_col(pawn, wk, bk, wa, wa2) pslice = wsq_to_pidx24(pawn) @@ -483,7 +483,7 @@ def kaakp_pctoindex(c: Request) -> int: bk = c.black_piece_squares[0] pawn = c.black_piece_squares[1] - pawn, wk, bk, wa, ba = flip_we_col(pawn, wk, bk, wa, ba) + pawn, wk, bk, wa, wa2 = flip_we_col(pawn, wk, bk, wa, wa2) pawn = flip_ns(pawn) pslice = wsq_to_pidx24(pawn) @@ -556,7 +556,7 @@ def kppka_pctoindex(c: Request) -> int: anchor, loosen = pp_putanchorfirst(pawn_a, pawn_b) - anchor, loosen, wk, bk, wa = flip_we_col(anchor, loosen, wk, bk, wa) + anchor, loosen, wk, bk, ba = flip_we_col(anchor, loosen, wk, bk, ba) i = wsq_to_pidx24(anchor) j = wsq_to_pidx48(loosen) From 08b6647d6fd5b6a512bbc9e1ab50222073f697bc Mon Sep 17 00:00:00 2001 From: Adrien Barbaresi Date: Mon, 6 Jan 2025 13:59:45 +0100 Subject: [PATCH 086/116] add permutations --- chess/gaviota.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chess/gaviota.py b/chess/gaviota.py index 9abe11bcb..a3372f04e 100644 --- a/chess/gaviota.py +++ b/chess/gaviota.py @@ -11,7 +11,7 @@ import struct import typing -from itertools import accumulate +from itertools import accumulate, permutations from types import TracebackType from typing import BinaryIO, Callable, Dict, List, Optional, Tuple, Type, Union @@ -172,8 +172,8 @@ def init_ppp48_idx() -> Tuple[List[List[List[int]]], List[int], List[int], List[ i, j, k = ITOSQ[x] - 8, ITOSQ[y] - 8, ITOSQ[z] - 8 if idx_is_empty(ppp48_idx[i][j][k]): - for p in [(i, j, k), (i, k, j), (j, i, k), (j, k, i), (k, i, j), (k, j, i)]: - ppp48_idx[p[0]][p[1]][p[2]] = idx + for ii, jj, kk in permutations((i, j, k)): + ppp48_idx[ii][jj][kk] = idx ppp48_sq_x[idx], ppp48_sq_y[idx], ppp48_sq_z[idx] = i, j, k idx += 1 From bbf2a05e6b6e3a83651b4333584940bc652cdea2 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 8 Jan 2025 20:30:04 +0100 Subject: [PATCH 087/116] Update source of Gaviota tables --- data/gaviota/SOURCE.txt | 290 +++++++++++++++++------------------ data/gaviota/TEST-SOURCE.txt | 94 ++++++------ 2 files changed, 192 insertions(+), 192 deletions(-) diff --git a/data/gaviota/SOURCE.txt b/data/gaviota/SOURCE.txt index dda9c83c7..706756322 100644 --- a/data/gaviota/SOURCE.txt +++ b/data/gaviota/SOURCE.txt @@ -1,145 +1,145 @@ -https://syzygy-tables.info/gaviota/kbk.gtb.cp4 -https://syzygy-tables.info/gaviota/knk.gtb.cp4 -https://syzygy-tables.info/gaviota/kpk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqk.gtb.cp4 -https://syzygy-tables.info/gaviota/krk.gtb.cp4 -https://syzygy-tables.info/gaviota/kbbk.gtb.cp4 -https://syzygy-tables.info/gaviota/kbkb.gtb.cp4 -https://syzygy-tables.info/gaviota/kbkn.gtb.cp4 -https://syzygy-tables.info/gaviota/kbkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kbnk.gtb.cp4 -https://syzygy-tables.info/gaviota/kbpk.gtb.cp4 -https://syzygy-tables.info/gaviota/knkn.gtb.cp4 -https://syzygy-tables.info/gaviota/knkp.gtb.cp4 -https://syzygy-tables.info/gaviota/knnk.gtb.cp4 -https://syzygy-tables.info/gaviota/knpk.gtb.cp4 -https://syzygy-tables.info/gaviota/kpkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kppk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqbk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqkb.gtb.cp4 -https://syzygy-tables.info/gaviota/kqkn.gtb.cp4 -https://syzygy-tables.info/gaviota/kqkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kqkq.gtb.cp4 -https://syzygy-tables.info/gaviota/kqkr.gtb.cp4 -https://syzygy-tables.info/gaviota/kqnk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqpk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqqk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqrk.gtb.cp4 -https://syzygy-tables.info/gaviota/krbk.gtb.cp4 -https://syzygy-tables.info/gaviota/krkb.gtb.cp4 -https://syzygy-tables.info/gaviota/krkn.gtb.cp4 -https://syzygy-tables.info/gaviota/krkp.gtb.cp4 -https://syzygy-tables.info/gaviota/krkr.gtb.cp4 -https://syzygy-tables.info/gaviota/krnk.gtb.cp4 -https://syzygy-tables.info/gaviota/krpk.gtb.cp4 -https://syzygy-tables.info/gaviota/krrk.gtb.cp4 -https://syzygy-tables.info/gaviota/kbbbk.gtb.cp4 -https://syzygy-tables.info/gaviota/kbbkb.gtb.cp4 -https://syzygy-tables.info/gaviota/kbbkn.gtb.cp4 -https://syzygy-tables.info/gaviota/kbbkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kbbkq.gtb.cp4 -https://syzygy-tables.info/gaviota/kbbkr.gtb.cp4 -https://syzygy-tables.info/gaviota/kbbnk.gtb.cp4 -https://syzygy-tables.info/gaviota/kbbpk.gtb.cp4 -https://syzygy-tables.info/gaviota/kbnkb.gtb.cp4 -https://syzygy-tables.info/gaviota/kbnkn.gtb.cp4 -https://syzygy-tables.info/gaviota/kbnkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kbnkq.gtb.cp4 -https://syzygy-tables.info/gaviota/kbnkr.gtb.cp4 -https://syzygy-tables.info/gaviota/kbnnk.gtb.cp4 -https://syzygy-tables.info/gaviota/kbnpk.gtb.cp4 -https://syzygy-tables.info/gaviota/kbpkb.gtb.cp4 -https://syzygy-tables.info/gaviota/kbpkn.gtb.cp4 -https://syzygy-tables.info/gaviota/kbpkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kbpkq.gtb.cp4 -https://syzygy-tables.info/gaviota/kbpkr.gtb.cp4 -https://syzygy-tables.info/gaviota/kbppk.gtb.cp4 -https://syzygy-tables.info/gaviota/knnkb.gtb.cp4 -https://syzygy-tables.info/gaviota/knnkn.gtb.cp4 -https://syzygy-tables.info/gaviota/knnkp.gtb.cp4 -https://syzygy-tables.info/gaviota/knnkq.gtb.cp4 -https://syzygy-tables.info/gaviota/knnkr.gtb.cp4 -https://syzygy-tables.info/gaviota/knnnk.gtb.cp4 -https://syzygy-tables.info/gaviota/knnpk.gtb.cp4 -https://syzygy-tables.info/gaviota/knpkb.gtb.cp4 -https://syzygy-tables.info/gaviota/knpkn.gtb.cp4 -https://syzygy-tables.info/gaviota/knpkp.gtb.cp4 -https://syzygy-tables.info/gaviota/knpkq.gtb.cp4 -https://syzygy-tables.info/gaviota/knpkr.gtb.cp4 -https://syzygy-tables.info/gaviota/knppk.gtb.cp4 -https://syzygy-tables.info/gaviota/kppkb.gtb.cp4 -https://syzygy-tables.info/gaviota/kppkn.gtb.cp4 -https://syzygy-tables.info/gaviota/kppkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kppkq.gtb.cp4 -https://syzygy-tables.info/gaviota/kppkr.gtb.cp4 -https://syzygy-tables.info/gaviota/kpppk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqbbk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqbkb.gtb.cp4 -https://syzygy-tables.info/gaviota/kqbkn.gtb.cp4 -https://syzygy-tables.info/gaviota/kqbkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kqbkq.gtb.cp4 -https://syzygy-tables.info/gaviota/kqbkr.gtb.cp4 -https://syzygy-tables.info/gaviota/kqbnk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqbpk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqnkb.gtb.cp4 -https://syzygy-tables.info/gaviota/kqnkn.gtb.cp4 -https://syzygy-tables.info/gaviota/kqnkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kqnkq.gtb.cp4 -https://syzygy-tables.info/gaviota/kqnkr.gtb.cp4 -https://syzygy-tables.info/gaviota/kqnnk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqnpk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqpkb.gtb.cp4 -https://syzygy-tables.info/gaviota/kqpkn.gtb.cp4 -https://syzygy-tables.info/gaviota/kqpkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kqpkq.gtb.cp4 -https://syzygy-tables.info/gaviota/kqpkr.gtb.cp4 -https://syzygy-tables.info/gaviota/kqppk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqqbk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqqkb.gtb.cp4 -https://syzygy-tables.info/gaviota/kqqkn.gtb.cp4 -https://syzygy-tables.info/gaviota/kqqkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kqqkq.gtb.cp4 -https://syzygy-tables.info/gaviota/kqqkr.gtb.cp4 -https://syzygy-tables.info/gaviota/kqqnk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqqpk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqqqk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqqrk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqrbk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqrkb.gtb.cp4 -https://syzygy-tables.info/gaviota/kqrkn.gtb.cp4 -https://syzygy-tables.info/gaviota/kqrkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kqrkq.gtb.cp4 -https://syzygy-tables.info/gaviota/kqrkr.gtb.cp4 -https://syzygy-tables.info/gaviota/kqrnk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqrpk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqrrk.gtb.cp4 -https://syzygy-tables.info/gaviota/krbbk.gtb.cp4 -https://syzygy-tables.info/gaviota/krbkb.gtb.cp4 -https://syzygy-tables.info/gaviota/krbkn.gtb.cp4 -https://syzygy-tables.info/gaviota/krbkp.gtb.cp4 -https://syzygy-tables.info/gaviota/krbkq.gtb.cp4 -https://syzygy-tables.info/gaviota/krbkr.gtb.cp4 -https://syzygy-tables.info/gaviota/krbnk.gtb.cp4 -https://syzygy-tables.info/gaviota/krbpk.gtb.cp4 -https://syzygy-tables.info/gaviota/krnkb.gtb.cp4 -https://syzygy-tables.info/gaviota/krnkn.gtb.cp4 -https://syzygy-tables.info/gaviota/krnkp.gtb.cp4 -https://syzygy-tables.info/gaviota/krnkq.gtb.cp4 -https://syzygy-tables.info/gaviota/krnkr.gtb.cp4 -https://syzygy-tables.info/gaviota/krnnk.gtb.cp4 -https://syzygy-tables.info/gaviota/krnpk.gtb.cp4 -https://syzygy-tables.info/gaviota/krpkb.gtb.cp4 -https://syzygy-tables.info/gaviota/krpkn.gtb.cp4 -https://syzygy-tables.info/gaviota/krpkp.gtb.cp4 -https://syzygy-tables.info/gaviota/krpkq.gtb.cp4 -https://syzygy-tables.info/gaviota/krpkr.gtb.cp4 -https://syzygy-tables.info/gaviota/krppk.gtb.cp4 -https://syzygy-tables.info/gaviota/krrbk.gtb.cp4 -https://syzygy-tables.info/gaviota/krrkb.gtb.cp4 -https://syzygy-tables.info/gaviota/krrkn.gtb.cp4 -https://syzygy-tables.info/gaviota/krrkp.gtb.cp4 -https://syzygy-tables.info/gaviota/krrkq.gtb.cp4 -https://syzygy-tables.info/gaviota/krrkr.gtb.cp4 -https://syzygy-tables.info/gaviota/krrnk.gtb.cp4 -https://syzygy-tables.info/gaviota/krrpk.gtb.cp4 -https://syzygy-tables.info/gaviota/krrrk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbbk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kpkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kppk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqbk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqqk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqrk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krbk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krrk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbbbk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbbkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbbkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbbkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbbkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbbkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbbnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbbpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbnkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbnkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbnkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbnkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbnkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbnnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbnpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbpkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbpkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbpkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbpkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbpkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbppk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knnkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knnkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knnkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knnkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knnkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knnnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knnpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knpkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knpkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knpkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knpkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knpkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knppk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kppkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kppkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kppkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kppkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kppkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kpppk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqbbk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqbkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqbkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqbkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqbkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqbkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqbnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqbpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqnkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqnkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqnkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqnkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqnkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqnnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqnpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqpkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqpkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqpkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqpkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqpkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqppk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqqbk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqqkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqqkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqqkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqqkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqqkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqqnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqqpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqqqk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqqrk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqrbk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqrkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqrkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqrkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqrkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqrkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqrnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqrpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqrrk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krbbk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krbkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krbkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krbkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krbkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krbkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krbnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krbpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krnkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krnkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krnkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krnkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krnkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krnnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krnpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krpkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krpkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krpkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krpkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krpkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krppk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krrbk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krrkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krrkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krrkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krrkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krrkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krrnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krrpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krrrk.gtb.cp4 diff --git a/data/gaviota/TEST-SOURCE.txt b/data/gaviota/TEST-SOURCE.txt index f7d29a546..36c83d39a 100644 --- a/data/gaviota/TEST-SOURCE.txt +++ b/data/gaviota/TEST-SOURCE.txt @@ -1,47 +1,47 @@ -https://syzygy-tables.info/gaviota/kbbk.gtb.cp4 -https://syzygy-tables.info/gaviota/kbkb.gtb.cp4 -https://syzygy-tables.info/gaviota/kbkn.gtb.cp4 -https://syzygy-tables.info/gaviota/kbkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kbnk.gtb.cp4 -https://syzygy-tables.info/gaviota/kbpk.gtb.cp4 -https://syzygy-tables.info/gaviota/knkn.gtb.cp4 -https://syzygy-tables.info/gaviota/knkp.gtb.cp4 -https://syzygy-tables.info/gaviota/knnk.gtb.cp4 -https://syzygy-tables.info/gaviota/knpk.gtb.cp4 -https://syzygy-tables.info/gaviota/kpkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kppk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqbk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqkb.gtb.cp4 -https://syzygy-tables.info/gaviota/kqkn.gtb.cp4 -https://syzygy-tables.info/gaviota/kqkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kqkq.gtb.cp4 -https://syzygy-tables.info/gaviota/kqkr.gtb.cp4 -https://syzygy-tables.info/gaviota/kqnk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqpk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqqk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqrk.gtb.cp4 -https://syzygy-tables.info/gaviota/krbk.gtb.cp4 -https://syzygy-tables.info/gaviota/krkb.gtb.cp4 -https://syzygy-tables.info/gaviota/krkn.gtb.cp4 -https://syzygy-tables.info/gaviota/krkp.gtb.cp4 -https://syzygy-tables.info/gaviota/krkr.gtb.cp4 -https://syzygy-tables.info/gaviota/krnk.gtb.cp4 -https://syzygy-tables.info/gaviota/krpk.gtb.cp4 -https://syzygy-tables.info/gaviota/krrk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqrnk.gtb.cp4 -https://syzygy-tables.info/gaviota/krpkn.gtb.cp4 -https://syzygy-tables.info/gaviota/kbpkp.gtb.cp4 -https://syzygy-tables.info/gaviota/krrpk.gtb.cp4 -https://syzygy-tables.info/gaviota/knppk.gtb.cp4 -https://syzygy-tables.info/gaviota/kbnkb.gtb.cp4 -https://syzygy-tables.info/gaviota/knnkn.gtb.cp4 -https://syzygy-tables.info/gaviota/krrnk.gtb.cp4 -https://syzygy-tables.info/gaviota/kqbbk.gtb.cp4 -https://syzygy-tables.info/gaviota/kppkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kppkq.gtb.cp4 -https://syzygy-tables.info/gaviota/krrrk.gtb.cp4 -https://syzygy-tables.info/gaviota/krnkp.gtb.cp4 -https://syzygy-tables.info/gaviota/kpppk.gtb.cp4 -https://syzygy-tables.info/gaviota/knnkp.gtb.cp4 -https://syzygy-tables.info/gaviota/krnpk.gtb.cp4 -https://syzygy-tables.info/gaviota/knpkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbbk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kpkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kppk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqbk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqqk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqrk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krbk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krkr.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krrk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqrnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krpkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbpkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krrpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knppk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kbnkb.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knnkn.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krrnk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kqbbk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kppkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kppkq.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krrrk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krnkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/kpppk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knnkp.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/krnpk.gtb.cp4 +https://tablebase.lichess.ovh/tables/standard/Gaviota/knpkp.gtb.cp4 From 3a974697b31e39ea8ad7e115350dc190464d7a76 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 11 Jan 2025 15:50:27 +0100 Subject: [PATCH 088/116] Use import from only for types --- chess/gaviota.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chess/gaviota.py b/chess/gaviota.py index a3372f04e..0bbe41b81 100644 --- a/chess/gaviota.py +++ b/chess/gaviota.py @@ -4,6 +4,7 @@ import ctypes.util import dataclasses import fnmatch +import itertools import logging import lzma import os @@ -11,7 +12,6 @@ import struct import typing -from itertools import accumulate, permutations from types import TracebackType from typing import BinaryIO, Callable, Dict, List, Optional, Tuple, Type, Union @@ -172,7 +172,7 @@ def init_ppp48_idx() -> Tuple[List[List[List[int]]], List[int], List[int], List[ i, j, k = ITOSQ[x] - 8, ITOSQ[y] - 8, ITOSQ[z] - 8 if idx_is_empty(ppp48_idx[i][j][k]): - for ii, jj, kk in permutations((i, j, k)): + for ii, jj, kk in itertools.permutations((i, j, k)): ppp48_idx[ii][jj][kk] = idx ppp48_sq_x[idx], ppp48_sq_y[idx], ppp48_sq_z[idx] = i, j, k idx += 1 @@ -204,7 +204,7 @@ def init_aaidx() -> Tuple[List[int], List[List[int]]]: def init_aaa() -> Tuple[List[int], List[List[int]]]: # Get aaa_base. comb = [a * (a - 1) // 2 for a in range(64)] - aaa_base = list(accumulate(comb)) + aaa_base = list(itertools.accumulate(comb)) # Get aaa_xyz. aaa_xyz = [[x, y, z] for z in range(64) for y in range(z) for x in range(y)] From 57536b1a0787cecf0a725ac50e9ce98e65340f11 Mon Sep 17 00:00:00 2001 From: "Robert Nurnberg @ elitebook" Date: Mon, 20 Jan 2025 08:49:28 +0100 Subject: [PATCH 089/116] fix typo in comment --- chess/syzygy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chess/syzygy.py b/chess/syzygy.py index 5ead9922c..c61890550 100644 --- a/chess/syzygy.py +++ b/chess/syzygy.py @@ -1850,7 +1850,7 @@ def probe_dtz(self, board: chess.Board) -> int: +-----+------------------+--------------------------------------------+ The return value can be off by one: a return value -n can mean a - losing zeroing move in in n + 1 plies and a return value +n can mean a + losing zeroing move in n + 1 plies and a return value +n can mean a winning zeroing move in n + 1 plies. This implies some primary tablebase lines may waste up to 1 ply. Rounding is never used for endgame phases where it would change the From 636e95fbf292f322fc4ab31b8c4add51f7534362 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sun, 23 Feb 2025 21:05:39 +0100 Subject: [PATCH 090/116] Revert simplifications of chess.gaviota from #1130 The resulting code is not equivalent, that is, when the full tablebase is available ./test.py GaviotaTestCase fails with ERROR: test_dm_4 (__main__.GaviotaTestCase.test_dm_4) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/niklas/Projekte/python-chess/./test.py", line 35, in _wrapper return f(self) File "/home/niklas/Projekte/python-chess/./test.py", line 4305, in test_dm_4 dtm = self.tablebase.probe_dtm(board) File "/home/niklas/Projekte/python-chess/chess/gaviota.py", line 1393, in probe_dtm dtm = self.egtb_get_dtm(req) File "/home/niklas/Projekte/python-chess/chess/gaviota.py", line 1518, in egtb_get_dtm dtm = self._tb_probe(req) File "/home/niklas/Projekte/python-chess/chess/gaviota.py", line 1621, in _tb_probe buffer_zipped = stream.read(z) ValueError: read length must be non-negative or -1 --- chess/gaviota.py | 553 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 374 insertions(+), 179 deletions(-) diff --git a/chess/gaviota.py b/chess/gaviota.py index 0bbe41b81..39173b593 100644 --- a/chess/gaviota.py +++ b/chess/gaviota.py @@ -4,7 +4,6 @@ import ctypes.util import dataclasses import fnmatch -import itertools import logging import lzma import os @@ -12,11 +11,11 @@ import struct import typing +import chess + from types import TracebackType from typing import BinaryIO, Callable, Dict, List, Optional, Tuple, Type, Union -import chess - LOGGER = logging.getLogger(__name__) @@ -110,19 +109,30 @@ def idx_is_empty(x: int) -> int: def flip_type(x: chess.Square, y: chess.Square) -> int: ret = 0 - file_x, rank_x = chess.square_file(x), chess.square_rank(x) - file_y, rank_y = chess.square_file(y), chess.square_rank(y) - if file_x > 3: - x, y = flip_we(x), flip_we(y) + if chess.square_file(x) > 3: + x = flip_we(x) + y = flip_we(y) ret |= 1 - if rank_x > 3: - x, y = flip_ns(x), flip_ns(y) + if chess.square_rank(x) > 3: + x = flip_ns(x) + y = flip_ns(y) ret |= 2 - if (rank_x, file_x) > (rank_y, file_y): - x, y = flip_nw_se(x), flip_nw_se(y) + rowx = chess.square_rank(x) + colx = chess.square_file(x) + + if rowx > colx: + x = flip_nw_se(x) + y = flip_nw_se(y) + ret |= 4 + + rowy = chess.square_rank(y) + coly = chess.square_file(y) + if rowx == colx and rowy > coly: + x = flip_nw_se(x) + y = flip_nw_se(y) ret |= 4 return ret @@ -135,6 +145,7 @@ def init_flipt() -> List[List[int]]: def init_pp48_idx() -> Tuple[List[List[int]], List[int], List[int]]: MAX_I = MAX_J = 48 + idx = 0 pp48_idx = [[-1] * MAX_J for _ in range(MAX_I)] pp48_sq_x = [NOSQUARE] * MAX_PP48_INDEX pp48_sq_y = [NOSQUARE] * MAX_PP48_INDEX @@ -146,8 +157,10 @@ def init_pp48_idx() -> Tuple[List[List[int]], List[int], List[int]]: j = flip_we(flip_ns(b)) - 8 if idx_is_empty(pp48_idx[i][j]): - pp48_idx[i][j] = pp48_idx[j][i] = idx - pp48_sq_x[idx], pp48_sq_y[idx] = i, j + pp48_idx[i][j] = idx + pp48_idx[j][i] = idx + pp48_sq_x[idx] = i + pp48_sq_y[idx] = j idx += 1 return pp48_idx, pp48_sq_x, pp48_sq_y @@ -166,15 +179,26 @@ def init_ppp48_idx() -> Tuple[List[List[List[int]]], List[int], List[int], List[ for x in range(48): for y in range(x + 1, 48): for z in range(y + 1, 48): - if not (in_queenside(ITOSQ[y]) and in_queenside(ITOSQ[z])): + a = ITOSQ[x] + b = ITOSQ[y] + c = ITOSQ[z] + if not in_queenside(b) or not in_queenside(c): continue - i, j, k = ITOSQ[x] - 8, ITOSQ[y] - 8, ITOSQ[z] - 8 + i = a - 8 + j = b - 8 + k = c - 8 if idx_is_empty(ppp48_idx[i][j][k]): - for ii, jj, kk in itertools.permutations((i, j, k)): - ppp48_idx[ii][jj][kk] = idx - ppp48_sq_x[idx], ppp48_sq_y[idx], ppp48_sq_z[idx] = i, j, k + ppp48_idx[i][j][k] = idx + ppp48_idx[i][k][j] = idx + ppp48_idx[j][i][k] = idx + ppp48_idx[j][k][i] = idx + ppp48_idx[k][i][j] = idx + ppp48_idx[k][j][i] = idx + ppp48_sq_x[idx] = i + ppp48_sq_y[idx] = j + ppp48_sq_z[idx] = k idx += 1 return ppp48_idx, ppp48_sq_x, ppp48_sq_y, ppp48_sq_z @@ -192,7 +216,8 @@ def init_aaidx() -> Tuple[List[int], List[List[int]]]: if idx_is_empty(aaidx[x][y]): # Still empty. - aaidx[x][y] = aaidx[y][x] = idx + aaidx[x][y] = idx + aaidx[y][x] = idx aabase[idx] = x idx += 1 @@ -204,10 +229,24 @@ def init_aaidx() -> Tuple[List[int], List[List[int]]]: def init_aaa() -> Tuple[List[int], List[List[int]]]: # Get aaa_base. comb = [a * (a - 1) // 2 for a in range(64)] - aaa_base = list(itertools.accumulate(comb)) + + accum = 0 + aaa_base = [0] * 64 + for a in range(64 - 1): + accum += comb[a] + aaa_base[a + 1] = accum # Get aaa_xyz. - aaa_xyz = [[x, y, z] for z in range(64) for y in range(z) for x in range(y)] + aaa_xyz = [[-1] * 3 for _ in range(MAX_AAAINDEX)] + + idx = 0 + for z in range(64): + for y in range(z): + for x in range(y): + aaa_xyz[idx][0] = x + aaa_xyz[idx][1] = y + aaa_xyz[idx][2] = z + idx += 1 return aaa_base, aaa_xyz @@ -312,33 +351,34 @@ def init_ppidx() -> Tuple[List[List[int]], List[int], List[int]]: def norm_kkindex(x: chess.Square, y: chess.Square) -> Tuple[int, int]: - file_x, rank_x = chess.square_file(x), chess.square_rank(x) - - if file_x > 3: - x, y = flip_we(x), flip_we(y) + if chess.square_file(x) > 3: + x = flip_we(x) + y = flip_we(y) - if rank_x > 3: - x, y = flip_ns(x), flip_ns(y) + if chess.square_rank(x) > 3: + x = flip_ns(x) + y = flip_ns(y) rowx = chess.square_rank(x) colx = chess.square_file(x) if rowx > colx: - x, y = flip_nw_se(x), flip_nw_se(y) + x = flip_nw_se(x) + y = flip_nw_se(y) rowy = chess.square_rank(y) coly = chess.square_file(y) if rowx == colx and rowy > coly: - x, y = flip_nw_se(x), flip_nw_se(y) + x = flip_nw_se(x) + y = flip_nw_se(y) return x, y - def init_kkidx() -> Tuple[List[List[int]], List[int], List[int]]: kkidx = [[-1] * 64 for _ in range(64)] - bksq, wksq = [-1] * MAX_KKINDEX, [-1] * MAX_KKINDEX - + bksq = [-1] * MAX_KKINDEX + wksq = [-1] * MAX_KKINDEX idx = 0 for x in range(64): for y in range(64): @@ -348,8 +388,10 @@ def init_kkidx() -> Tuple[List[List[int]], List[int], List[int]]: i, j = norm_kkindex(x, y) if idx_is_empty(kkidx[i][j]): - kkidx[i][j] = kkidx[x][y] = idx - bksq[idx], wksq[idx] = i, j + kkidx[i][j] = idx + kkidx[x][y] = idx + bksq[idx] = i + wksq[idx] = j idx += 1 return kkidx, wksq, bksq @@ -362,12 +404,20 @@ def kxk_pctoindex(c: Request) -> int: ft = flip_type(c.black_piece_squares[0], c.white_piece_squares[0]) - ws, bs = c.white_piece_squares, c.black_piece_squares + ws = c.white_piece_squares + bs = c.black_piece_squares + + if (ft & 1) != 0: + ws = [flip_we(b) for b in ws] + bs = [flip_we(b) for b in bs] + + if (ft & 2) != 0: + ws = [flip_ns(b) for b in ws] + bs = [flip_ns(b) for b in bs] - for f, flip in [(1, flip_we), (2, flip_ns), (4, flip_nw_se)]: - if ft & f: - ws = [flip(b) for b in ws] - bs = [flip(b) for b in bs] + if (ft & 4) != 0: + ws = [flip_nw_se(b) for b in ws] + bs = [flip_nw_se(b) for b in bs] ki = KKIDX[bs[0]][ws[0]] # KKIDX[black king][white king] @@ -376,14 +426,6 @@ def kxk_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ws[1] - -def flip_we_col(*pieces: int) -> tuple[int, ...]: - if (pieces[0] & 7) > 3: - # Column is more than 3, i.e., e, f, g or h. - pieces = tuple(flip_we(piece) for piece in pieces) - return pieces - - def kapkb_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 * 64 * 64 BLOCK_B = 64 * 64 * 64 @@ -396,17 +438,24 @@ def kapkb_pctoindex(c: Request) -> int: bk = c.black_piece_squares[0] ba = c.black_piece_squares[1] - if not chess.A2 <= pawn < chess.A8: + if not (chess.A2 <= pawn < chess.A8): return NOINDEX - pawn, wk, bk, wa, ba = flip_we_col(pawn, wk, bk, wa, ba) + if (pawn & 7) > 3: + # Column is more than 3, i.e., e, f, g or h. + pawn = flip_we(pawn) + wk = flip_we(wk) + bk = flip_we(bk) + wa = flip_we(wa) + ba = flip_we(ba) - # flip_ns, down one row - pslice = ((pawn ^ 56) - 8 + (pawn & 3)) >> 1 + sq = pawn + sq ^= 56 # flip_ns + sq -= 8 # Down one row + pslice = (sq + (sq & 3)) >> 1 return pslice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + wa * BLOCK_D + ba - def kabpk_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 * 64 * 64 BLOCK_B = 64 * 64 * 64 @@ -419,13 +468,18 @@ def kabpk_pctoindex(c: Request) -> int: pawn = c.white_piece_squares[3] bk = c.black_piece_squares[0] - pawn, wk, bk, wa, wb = flip_we_col(pawn, wk, bk, wa, wb) + if (pawn & 7) > 3: + # Column is more than 3, i.e., e, f, g or h. + pawn = flip_we(pawn) + wk = flip_we(wk) + bk = flip_we(bk) + wa = flip_we(wa) + wb = flip_we(wb) pslice = wsq_to_pidx24(pawn) return pslice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + wa * BLOCK_D + wb - def kabkp_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 * 64 * 64 BLOCK_B = 64 * 64 * 64 @@ -438,17 +492,23 @@ def kabkp_pctoindex(c: Request) -> int: bk = c.black_piece_squares[0] wb = c.white_piece_squares[2] - if not chess.A2 <= pawn < chess.A8: + if not (chess.A2 <= pawn < chess.A8): return NOINDEX - pawn, wk, bk, wa, wb = flip_we_col(pawn, wk, bk, wa, wb) + if (pawn & 7) > 3: + # Column is more than 3, i.e., e, f, g or h. + pawn = flip_we(pawn) + wk = flip_we(wk) + bk = flip_we(bk) + wa = flip_we(wa) + wb = flip_we(wb) - # Down one row - pslice = ((pawn - 8) + (pawn & 3)) >> 1 + sq = pawn + sq -= 8 # Down one row + pslice = (sq + (sq & 3)) >> 1 return pslice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + wa * BLOCK_D + wb - def kaapk_pctoindex(c: Request) -> int: BLOCK_C = MAX_AAINDEX BLOCK_B = 64 * BLOCK_C @@ -460,7 +520,13 @@ def kaapk_pctoindex(c: Request) -> int: pawn = c.white_piece_squares[3] bk = c.black_piece_squares[0] - pawn, wk, bk, wa, wa2 = flip_we_col(pawn, wk, bk, wa, wa2) + if (pawn & 7) > 3: + # Column is more than 3, i.e., e, f, g or h. + pawn = flip_we(pawn) + wk = flip_we(wk) + bk = flip_we(bk) + wa = flip_we(wa) + wa2 = flip_we(wa2) pslice = wsq_to_pidx24(pawn) @@ -471,7 +537,6 @@ def kaapk_pctoindex(c: Request) -> int: return pslice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + aa_combo - def kaakp_pctoindex(c: Request) -> int: BLOCK_C = MAX_AAINDEX BLOCK_B = 64 * BLOCK_C @@ -483,7 +548,13 @@ def kaakp_pctoindex(c: Request) -> int: bk = c.black_piece_squares[0] pawn = c.black_piece_squares[1] - pawn, wk, bk, wa, wa2 = flip_we_col(pawn, wk, bk, wa, wa2) + if (pawn & 7) > 3: + # Column is more than 3, i.e., e, f, g or h. + pawn = flip_we(pawn) + wk = flip_we(wk) + bk = flip_we(bk) + wa = flip_we(wa) + wa2 = flip_we(wa2) pawn = flip_ns(pawn) pslice = wsq_to_pidx24(pawn) @@ -495,7 +566,6 @@ def kaakp_pctoindex(c: Request) -> int: return pslice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + aa_combo - def kapkp_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 * 64 BLOCK_B = 64 * 64 @@ -503,20 +573,30 @@ def kapkp_pctoindex(c: Request) -> int: wk = c.white_piece_squares[0] wa = c.white_piece_squares[1] - anchor = c.white_piece_squares[2] + pawn_a = c.white_piece_squares[2] bk = c.black_piece_squares[0] - loosen = c.black_piece_squares[1] + pawn_b = c.black_piece_squares[1] + + anchor = pawn_a + loosen = pawn_b - anchor, loosen, wk, bk, wa = flip_we_col(anchor, loosen, wk, bk, wa) + if (anchor & 7) > 3: + # Column is more than 3, i.e., e, f, g or h. + anchor = flip_we(anchor) + loosen = flip_we(loosen) + wk = flip_we(wk) + bk = flip_we(bk) + wa = flip_we(wa) - pp_slice = wsq_to_pidx24(anchor) * 48 + loosen - 8 + m = wsq_to_pidx24(anchor) + n = loosen - 8 + pp_slice = m * 48 + n if idx_is_empty(pp_slice): return NOINDEX return pp_slice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + wa - def kappk_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 * 64 BLOCK_B = 64 * 64 @@ -530,7 +610,13 @@ def kappk_pctoindex(c: Request) -> int: anchor, loosen = pp_putanchorfirst(pawn_a, pawn_b) - anchor, loosen, wk, bk, wa = flip_we_col(anchor, loosen, wk, bk, wa) + if (anchor & 7) > 3: + # Column is more than 3, i.e., e, f, g or h. + anchor = flip_we(anchor) + loosen = flip_we(loosen) + wk = flip_we(wk) + bk = flip_we(bk) + wa = flip_we(wa) i = wsq_to_pidx24(anchor) j = wsq_to_pidx48(loosen) @@ -542,7 +628,6 @@ def kappk_pctoindex(c: Request) -> int: return pp_slice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + wa - def kppka_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 * 64 BLOCK_B = 64 * 64 @@ -556,7 +641,12 @@ def kppka_pctoindex(c: Request) -> int: anchor, loosen = pp_putanchorfirst(pawn_a, pawn_b) - anchor, loosen, wk, bk, ba = flip_we_col(anchor, loosen, wk, bk, ba) + if (anchor & 7) > 3: + anchor = flip_we(anchor) + loosen = flip_we(loosen) + wk = flip_we(wk) + bk = flip_we(bk) + ba = flip_we(ba) i = wsq_to_pidx24(anchor) j = wsq_to_pidx48(loosen) @@ -568,7 +658,6 @@ def kppka_pctoindex(c: Request) -> int: return pp_slice * BLOCK_A + wk * BLOCK_B + bk * BLOCK_C + ba - def kabck_pctoindex(c: Request) -> int: N_WHITE = 4 N_BLACK = 1 @@ -600,7 +689,6 @@ def kabck_pctoindex(c: Request) -> int: return ki * BLOCK_A + ws[1] * BLOCK_B + ws[2] * BLOCK_C + ws[3] - def kabbk_pctoindex(c: Request) -> int: N_WHITE = 4 N_BLACK = 1 @@ -632,7 +720,6 @@ def kabbk_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ai * BLOCK_Bx + ws[1] - def kaabk_pctoindex(c: Request) -> int: N_WHITE = 4 N_BLACK = 1 @@ -664,23 +751,45 @@ def kaabk_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ai * BLOCK_Bx + ws[3] - def aaa_getsubi(x: int, y: int, z: int) -> int: - return x + (y - 1) * y // 2 + AAA_BASE[z] - + bse = AAA_BASE[z] + calc_idx = x + (y - 1) * y // 2 + bse + return calc_idx def kaaak_pctoindex(c: Request) -> int: + N_WHITE = 4 + N_BLACK = 1 BLOCK_Ax = MAX_AAAINDEX - ws = c.white_piece_squares[:4] - bs = c.black_piece_squares[:1] + + ws = c.white_piece_squares[:N_WHITE] + bs = c.black_piece_squares[:N_BLACK] + ft = FLIPT[c.black_piece_squares[0]][c.white_piece_squares[0]] - for flag, flip in [(WE_FLAG, flip_we), (NS_FLAG, flip_ns), (NW_SE_FLAG, flip_nw_se)]: - if ft & flag: - ws = [flip(i) for i in ws] - bs = [flip(i) for i in bs] + if (ft & WE_FLAG) != 0: + ws = [flip_we(i) for i in ws] + bs = [flip_we(i) for i in bs] + + if (ft & NS_FLAG) != 0: + ws = [flip_ns(i) for i in ws] + bs = [flip_ns(i) for i in bs] - ws[1:4] = sorted(ws[1:4]) + if (ft & NW_SE_FLAG) != 0: + ws = [flip_nw_se(i) for i in ws] + bs = [flip_nw_se(i) for i in bs] + + if ws[2] < ws[1]: + tmp = ws[1] + ws[1] = ws[2] + ws[2] = tmp + if ws[3] < ws[2]: + tmp = ws[2] + ws[2] = ws[3] + ws[3] = tmp + if ws[2] < ws[1]: + tmp = ws[1] + ws[1] = ws[2] + ws[2] = tmp ki = KKIDX[bs[0]][ws[0]] @@ -694,7 +803,6 @@ def kaaak_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ai - def kppkp_pctoindex(c: Request) -> int: BLOCK_Ax = MAX_PP48_INDEX * 64 * 64 BLOCK_Bx = 64 * 64 @@ -706,7 +814,12 @@ def kppkp_pctoindex(c: Request) -> int: bk = c.black_piece_squares[0] pawn_c = c.black_piece_squares[1] - pawn_c, wk, pawn_a, pawn_b, bk = flip_we_col(pawn_c, wk, pawn_a, pawn_b, bk) + if (pawn_c & 7) > 3: + wk = flip_we(wk) + pawn_a = flip_we(pawn_a) + pawn_b = flip_we(pawn_b) + bk = flip_we(bk) + pawn_c = flip_we(pawn_c) i = flip_we(flip_ns(pawn_a)) - 8 j = flip_we(flip_ns(pawn_b)) - 8 @@ -721,19 +834,28 @@ def kppkp_pctoindex(c: Request) -> int: return k * BLOCK_Ax + pp48_slice * BLOCK_Bx + wk * BLOCK_Cx + bk - def kaakb_pctoindex(c: Request) -> int: + N_WHITE = 3 + N_BLACK = 2 BLOCK_Bx = 64 BLOCK_Ax = BLOCK_Bx * MAX_AAINDEX - ws = c.white_piece_squares[:3] - bs = c.black_piece_squares[:2] ft = FLIPT[c.black_piece_squares[0]][c.white_piece_squares[0]] - for flag, flip in [(WE_FLAG, flip_we), (NS_FLAG, flip_ns), (NW_SE_FLAG, flip_nw_se)]: - if ft & flag: - ws = [flip(i) for i in ws] - bs = [flip(i) for i in bs] + ws = c.white_piece_squares[:N_WHITE] + bs = c.black_piece_squares[:N_BLACK] + + if (ft & WE_FLAG) != 0: + ws = [flip_we(i) for i in ws] + bs = [flip_we(i) for i in bs] + + if (ft & NS_FLAG) != 0: + ws = [flip_ns(i) for i in ws] + bs = [flip_ns(i) for i in bs] + + if (ft & NW_SE_FLAG) != 0: + ws = [flip_nw_se(i) for i in ws] + bs = [flip_nw_se(i) for i in bs] ki = KKIDX[bs[0]][ws[0]] # KKIDX[black king][white king] ai = AAIDX[ws[1]][ws[2]] @@ -743,7 +865,6 @@ def kaakb_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ai * BLOCK_Bx + bs[1] - def kabkc_pctoindex(c: Request) -> int: N_WHITE = 3 N_BLACK = 2 @@ -757,10 +878,17 @@ def kabkc_pctoindex(c: Request) -> int: ws = c.white_piece_squares[:N_WHITE] bs = c.black_piece_squares[:N_BLACK] - for flag, flip in [(WE_FLAG, flip_we), (NS_FLAG, flip_ns), (NW_SE_FLAG, flip_nw_se)]: - if ft & flag: - ws = [flip(i) for i in ws] - bs = [flip(i) for i in bs] + if (ft & WE_FLAG) != 0: + ws = [flip_we(i) for i in ws] + bs = [flip_we(i) for i in bs] + + if (ft & NS_FLAG) != 0: + ws = [flip_ns(i) for i in ws] + bs = [flip_ns(i) for i in bs] + + if (ft & NW_SE_FLAG) != 0: + ws = [flip_nw_se(i) for i in ws] + bs = [flip_nw_se(i) for i in bs] ki = KKIDX[bs[0]][ws[0]] # KKIDX [black king] [white king] @@ -769,17 +897,23 @@ def kabkc_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ws[1] * BLOCK_Bx + ws[2] * BLOCK_Cx + bs[1] - def kpkp_pctoindex(c: Request) -> int: BLOCK_Ax = 64 * 64 BLOCK_Bx = 64 wk = c.white_piece_squares[0] bk = c.black_piece_squares[0] - anchor = c.white_piece_squares[1] - loosen = c.black_piece_squares[1] + pawn_a = c.white_piece_squares[1] + pawn_b = c.black_piece_squares[1] - anchor, loosen, wk, bk = flip_we_col(anchor, loosen, wk, bk) + anchor = pawn_a + loosen = pawn_b + + if (anchor & 7) > 3: + anchor = flip_we(anchor) + loosen = flip_we(loosen) + wk = flip_we(wk) + bk = flip_we(bk) m = wsq_to_pidx24(anchor) n = loosen - 8 @@ -791,7 +925,6 @@ def kpkp_pctoindex(c: Request) -> int: return pp_slice * BLOCK_Ax + wk * BLOCK_Bx + bk - def kppk_pctoindex(c: Request) -> int: BLOCK_Ax = 64 * 64 BLOCK_Bx = 64 @@ -802,7 +935,11 @@ def kppk_pctoindex(c: Request) -> int: anchor, loosen = pp_putanchorfirst(pawn_a, pawn_b) - anchor, loosen, wk, bk = flip_we_col(anchor, loosen, wk, bk) + if (anchor & 7) > 3: + anchor = flip_we(anchor) + loosen = flip_we(loosen) + wk = flip_we(wk) + bk = flip_we(bk) i = wsq_to_pidx24(anchor) j = wsq_to_pidx48(loosen) @@ -814,7 +951,6 @@ def kppk_pctoindex(c: Request) -> int: return pp_slice * BLOCK_Ax + wk * BLOCK_Bx + bk - def kapk_pctoindex(c: Request) -> int: BLOCK_Ax = 64 * 64 * 64 BLOCK_Bx = 64 * 64 @@ -825,17 +961,22 @@ def kapk_pctoindex(c: Request) -> int: wk = c.white_piece_squares[0] bk = c.black_piece_squares[0] - if not chess.A2 <= pawn < chess.A8: + if not (chess.A2 <= pawn < chess.A8): return NOINDEX - pawn, wk, bk, wa = flip_we_col(pawn, wk, bk, wa) + if (pawn & 7) > 3: + pawn = flip_we(pawn) + wk = flip_we(wk) + bk = flip_we(bk) + wa = flip_we(wa) - sq = pawn ^ 56 - 8 # flip_ns and down one row + sq = pawn + sq ^= 56 # flip_ns + sq -= 8 # Down one row pslice = ((sq + (sq & 3)) >> 1) return pslice * BLOCK_Ax + wk * BLOCK_Bx + bk * BLOCK_Cx + wa - def kabk_pctoindex(c: Request) -> int: BLOCK_Ax = 64 * 64 BLOCK_Bx = 64 @@ -845,10 +986,17 @@ def kabk_pctoindex(c: Request) -> int: ws = c.white_piece_squares bs = c.black_piece_squares - for flag, flip in [(1, flip_we), (2, flip_ns), (4, flip_nw_se)]: - if ft & flag: - ws = [flip(b) for b in ws] - bs = [flip(b) for b in bs] + if (ft & 1) != 0: + ws = [flip_we(b) for b in ws] + bs = [flip_we(b) for b in bs] + + if (ft & 2) != 0: + ws = [flip_ns(b) for b in ws] + bs = [flip_ns(b) for b in bs] + + if (ft & 4) != 0: + ws = [flip_nw_se(b) for b in ws] + bs = [flip_nw_se(b) for b in bs] ki = KKIDX[bs[0]][ws[0]] # KKIDX[black king][white king] @@ -857,7 +1005,6 @@ def kabk_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ws[1] * BLOCK_Bx + ws[2] - def kakp_pctoindex(c: Request) -> int: BLOCK_Ax = 64 * 64 * 64 BLOCK_Bx = 64 * 64 @@ -868,29 +1015,42 @@ def kakp_pctoindex(c: Request) -> int: wk = c.white_piece_squares[0] bk = c.black_piece_squares[0] - if not chess.A2 <= pawn < chess.A8: + if not (chess.A2 <= pawn < chess.A8): return NOINDEX - pawn, wk, bk, wa = flip_we_col(pawn, wk, bk, wa) + if (pawn & 7) > 3: + pawn = flip_we(pawn) + wk = flip_we(wk) + bk = flip_we(bk) + wa = flip_we(wa) - # Down one row - pslice = ((pawn - 8) + ((pawn - 8) & 3)) >> 1 + sq = pawn + sq -= 8 # Down one row + pslice = (sq + (sq & 3)) >> 1 return pslice * BLOCK_Ax + wk * BLOCK_Bx + bk * BLOCK_Cx + wa - def kaak_pctoindex(c: Request) -> int: + N_WHITE = 3 + N_BLACK = 1 BLOCK_Ax = MAX_AAINDEX ft = FLIPT[c.black_piece_squares[0]][c.white_piece_squares[0]] - ws = c.white_piece_squares[:3] - bs = c.black_piece_squares[:1] + ws = c.white_piece_squares[:N_WHITE] + bs = c.black_piece_squares[:N_BLACK] + + if (ft & WE_FLAG) != 0: + ws = [flip_we(i) for i in ws] + bs = [flip_we(i) for i in bs] - for flag, flip in [(WE_FLAG, flip_we), (NS_FLAG, flip_ns), (NW_SE_FLAG, flip_nw_se)]: - if ft & flag: - ws = [flip(i) for i in ws] - bs = [flip(i) for i in bs] + if (ft & NS_FLAG) != 0: + ws = [flip_ns(i) for i in ws] + bs = [flip_ns(i) for i in bs] + + if (ft & NW_SE_FLAG) != 0: + ws = [flip_nw_se(i) for i in ws] + bs = [flip_nw_se(i) for i in bs] ki = KKIDX[bs[0]][ws[0]] # KKIDX[black king][white king] ai = AAIDX[ws[1]][ws[2]] @@ -900,7 +1060,6 @@ def kaak_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ai - def kakb_pctoindex(c: Request) -> int: BLOCK_Ax = 64 * 64 BLOCK_Bx = 64 @@ -910,9 +1069,23 @@ def kakb_pctoindex(c: Request) -> int: ws = c.white_piece_squares[:] bs = c.black_piece_squares[:] - for bit, flip in [(1, flip_we), (2, flip_ns), (4, flip_nw_se)]: - if ft & bit: - ws[0], ws[1], bs[0], bs[1] = flip(ws[0]), flip(ws[1]), flip(bs[0]), flip(bs[1]) + if (ft & 1) != 0: + ws[0] = flip_we(ws[0]) + ws[1] = flip_we(ws[1]) + bs[0] = flip_we(bs[0]) + bs[1] = flip_we(bs[1]) + + if (ft & 2) != 0: + ws[0] = flip_ns(ws[0]) + ws[1] = flip_ns(ws[1]) + bs[0] = flip_ns(bs[0]) + bs[1] = flip_ns(bs[1]) + + if (ft & 4) != 0: + ws[0] = flip_nw_se(ws[0]) + ws[1] = flip_nw_se(ws[1]) + bs[0] = flip_nw_se(bs[0]) + bs[1] = flip_nw_se(bs[1]) ki = KKIDX[bs[0]][ws[0]] # KKIDX[black king][white king] @@ -921,7 +1094,6 @@ def kakb_pctoindex(c: Request) -> int: return ki * BLOCK_Ax + ws[1] * BLOCK_Bx + bs[1] - def kpk_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 BLOCK_B = 64 @@ -930,16 +1102,21 @@ def kpk_pctoindex(c: Request) -> int: wk = c.white_piece_squares[0] bk = c.black_piece_squares[0] - if not chess.A2 <= pawn < chess.A8: + if not (chess.A2 <= pawn < chess.A8): return NOINDEX - pawn, wk, bk = flip_we_col(pawn, wk, bk) + if (pawn & 7) > 3: + pawn = flip_we(pawn) + wk = flip_we(wk) + bk = flip_we(bk) - sq = (pawn ^ 56) - 8 # flip_ns, down one row + sq = pawn + sq ^= 56 # flip_ns + sq -= 8 # Down one row pslice = ((sq + (sq & 3)) >> 1) - return pslice * BLOCK_A + wk * BLOCK_B + bk - + res = pslice * BLOCK_A + wk * BLOCK_B + bk + return res def kpppk_pctoindex(c: Request) -> int: BLOCK_A = 64 * 64 @@ -949,16 +1126,26 @@ def kpppk_pctoindex(c: Request) -> int: pawn_a = c.white_piece_squares[1] pawn_b = c.white_piece_squares[2] pawn_c = c.white_piece_squares[3] + bk = c.black_piece_squares[0] - i, j, k = [x - 8 for x in (pawn_a, pawn_b, pawn_c)] + i = pawn_a - 8 + j = pawn_b - 8 + k = pawn_c - 8 + ppp48_slice = PPP48_IDX[i][j][k] if idx_is_empty(ppp48_slice): - wk, pawn_a, pawn_b, pawn_c, bk = [flip_we(x) for x in (wk, pawn_a, pawn_b, pawn_c, bk)] + wk = flip_we(wk) + pawn_a = flip_we(pawn_a) + pawn_b = flip_we(pawn_b) + pawn_c = flip_we(pawn_c) + bk = flip_we(bk) + + i = pawn_a - 8 + j = pawn_b - 8 + k = pawn_c - 8 - # removing this doesn't impact the tests? - i, j, k = [x - 8 for x in (pawn_a, pawn_b, pawn_c)] ppp48_slice = PPP48_IDX[i][j][k] if idx_is_empty(ppp48_slice): @@ -1143,15 +1330,13 @@ def __init__(self, maxindex: int, slice_n: int, pctoi: Callable[[Request], int]) def sortlists(ws: List[int], wp: List[int]) -> Tuple[List[int], List[int]]: - z = sorted(zip(wp, ws), reverse=True) + z = sorted(zip(wp, ws), key=lambda x: x[0], reverse=True) wp2, ws2 = zip(*z) return list(ws2), list(wp2) - def egtb_block_unpack(side: int, n: int, bp: bytes) -> List[int]: return [dtm_unpack(side, i) for i in bp[:n]] - def split_index(i: int) -> Tuple[int, int]: return divmod(i, ENTRIES_PER_BLOCK) @@ -1175,23 +1360,19 @@ def removepiece(ys: List[int], yp: List[int], j: int) -> None: del ys[j] del yp[j] - def opp(side: int) -> int: return 1 if side == 0 else 0 - def adjust_up(dist: int) -> int: - if (dist & INFOMASK) in [iWMATE, iWMATEt, iBMATE, iBMATEt]: - return dist + (1 << PLYSHIFT) - return dist + udist = dist + sw = udist & INFOMASK + if sw in [iWMATE, iWMATEt, iBMATE, iBMATEt]: + udist += (1 << PLYSHIFT) -def bestx(side: int, a: int, b: int) -> int: - if a == iFORBID: - return b - if b == iFORBID: - return a + return udist +def bestx(side: int, a: int, b: int) -> int: # 0 = selectfirst # 1 = selectlowest # 2 = selecthighest @@ -1204,25 +1385,33 @@ def bestx(side: int, a: int, b: int) -> int: [3, 3, 3, 0], # forbid ] + xorkey = [0, 3] + + if a == iFORBID: + return b + if b == iFORBID: + return a + retu = [a, a, b, b] if b < a: - retu[1:2] = [b, a] + retu[1] = b + retu[2] = a - key = comparison[a & 3][b & 3] ^ [0, 3][side] # ^ xorkey + key = comparison[a & 3][b & 3] ^ xorkey[side] return retu[key] - def unpackdist(d: int) -> Tuple[int, int]: return d >> PLYSHIFT, d & INFOMASK - def dtm_unpack(stm: int, packed: int) -> int: - if packed in [iDRAW, iFORBID]: - return packed + p = packed + + if p in [iDRAW, iFORBID]: + return p - info = packed & 3 - store = packed >> 2 + info = p & 3 + store = p >> 2 if stm == 0: if info == iWMATE: @@ -1398,23 +1587,28 @@ def probe_dtm(self, board: chess.Board) -> int: if req.realside == 1: if req.is_reversed: return ply - return -ply - if req.is_reversed: - return -ply - return ply - - if res == iBMATE: + else: + return -ply + else: + if req.is_reversed: + return -ply + else: + return ply + elif res == iBMATE: # Black mates in the stored position. if req.realside == 0: if req.is_reversed: return ply - return -ply - if req.is_reversed: - return -ply - return ply - - # Draw. - return 0 + else: + return -ply + else: + if req.is_reversed: + return -ply + else: + return ply + else: + # Draw. + return 0 def get_dtm(self, board: chess.Board, default: Optional[int] = None) -> Optional[int]: try: @@ -1450,12 +1644,12 @@ def probe_wdl(self, board: chess.Board) -> int: if dtm == 0: if board.is_checkmate(): return -1 - return 0 - - if dtm > 0: + else: + return 0 + elif dtm > 0: return 1 - - return -1 + else: + return -1 def get_wdl(self, board: chess.Board, default: Optional[int] = None) -> Optional[int]: try: @@ -1601,7 +1795,8 @@ def egtb_block_getsize(self, req: Request, idx: int) -> int: if (offset + blocksz) > maxindex: return maxindex - offset # Last block size - return blocksz # Size of a normal block + else: + return blocksz # Size of a normal block def _tb_probe(self, req: Request) -> int: stream = self._setup_tablebase(req) From dd4d9c1285d70f1aaffa276244101d9373053c1d Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sun, 23 Feb 2025 22:55:23 +0100 Subject: [PATCH 091/116] Fix ep resolution in chess.gaviota.PythonTablebase (fixes #1132) --- chess/gaviota.py | 142 +++++++---------------------------------------- test.py | 5 ++ 2 files changed, 26 insertions(+), 121 deletions(-) diff --git a/chess/gaviota.py b/chess/gaviota.py index 39173b593..83fb7bb64 100644 --- a/chess/gaviota.py +++ b/chess/gaviota.py @@ -1356,51 +1356,9 @@ def split_index(i: int) -> Tuple[int, int]: iBMATEt = tb_BMATE | 4 -def removepiece(ys: List[int], yp: List[int], j: int) -> None: - del ys[j] - del yp[j] - def opp(side: int) -> int: return 1 if side == 0 else 0 -def adjust_up(dist: int) -> int: - udist = dist - sw = udist & INFOMASK - - if sw in [iWMATE, iWMATEt, iBMATE, iBMATEt]: - udist += (1 << PLYSHIFT) - - return udist - -def bestx(side: int, a: int, b: int) -> int: - # 0 = selectfirst - # 1 = selectlowest - # 2 = selecthighest - # 3 = selectsecond - comparison = [ - # draw, wmate, bmate, forbid - [0, 3, 0, 0], # draw - [0, 1, 0, 0], # wmate - [3, 3, 2, 0], # bmate - [3, 3, 3, 0], # forbid - ] - - xorkey = [0, 3] - - if a == iFORBID: - return b - if b == iFORBID: - return a - - retu = [a, a, b, b] - - if b < a: - retu[1] = b - retu[2] = a - - key = comparison[a & 3][b & 3] ^ xorkey[side] - return retu[key] - def unpackdist(d: int) -> Tuple[int, int]: return d >> PLYSHIFT, d & INFOMASK @@ -1492,12 +1450,11 @@ class Request: black_piece_types: List[int] is_reversed: bool - def __init__(self, white_squares: List[int], white_types: List[chess.PieceType], black_squares: List[int], black_types: List[chess.PieceType], side: int, epsq: int): + def __init__(self, white_squares: List[int], white_types: List[chess.PieceType], black_squares: List[int], black_types: List[chess.PieceType], side: int): self.white_squares, self.white_types = sortlists(white_squares, white_types) self.black_squares, self.black_types = sortlists(black_squares, black_types) self.realside = side self.side = side - self.epsq = epsq @dataclasses.dataclass @@ -1569,17 +1526,34 @@ def probe_dtm(self, board: chess.Board) -> int: if board.occupied == board.kings: return 0 + # Resolve en passant. + dtm = self._probe_dtm_no_ep(board) + for move in board.generate_legal_ep(): + try: + board.push(move) + + child_dtm = -self._probe_dtm_no_ep(board) + if child_dtm > 0: + child_dtm += 1 + elif child_dtm < 0: + child_dtm -= 1 + + dtm = min(dtm, child_dtm) if dtm * child_dtm > 0 else max(dtm, child_dtm) + finally: + board.pop() + return dtm + + def _probe_dtm_no_ep(self, board: chess.Board) -> int: # Prepare the tablebase request. white_squares = list(chess.SquareSet(board.occupied_co[chess.WHITE])) white_types = [typing.cast(chess.PieceType, board.piece_type_at(sq)) for sq in white_squares] black_squares = list(chess.SquareSet(board.occupied_co[chess.BLACK])) black_types = [typing.cast(chess.PieceType, board.piece_type_at(sq)) for sq in black_squares] side = 0 if (board.turn == chess.WHITE) else 1 - epsq = board.ep_square if board.ep_square else NOSQUARE - req = Request(white_squares, white_types, black_squares, black_types, side, epsq) + req = Request(white_squares, white_types, black_squares, black_types, side) # Probe. - dtm = self.egtb_get_dtm(req) + dtm = self._tb_probe(req) ply, res = unpackdist(dtm) if res == iWMATE: @@ -1675,10 +1649,7 @@ def _setup_tablebase(self, req: Request) -> BinaryIO: req.white_piece_types = req.black_types req.black_piece_squares = [flip_ns(s) for s in req.white_squares] req.black_piece_types = req.white_types - req.side = opp(req.side) - if req.epsq != NOSQUARE: - req.epsq = flip_ns(req.epsq) else: raise MissingTableError(f"no gaviota table available for: {white_letters.upper()}v{black_letters.upper()}") @@ -1708,77 +1679,6 @@ def close(self) -> None: _, stream = self.streams.popitem() stream.close() - def egtb_get_dtm(self, req: Request) -> int: - dtm = self._tb_probe(req) - - if req.epsq != NOSQUARE: - capturer_a = 0 - capturer_b = 0 - xed = 0 - - # Flip for move generation. - if req.side == 0: - xs = list(req.white_piece_squares) - xp = list(req.white_piece_types) - ys = list(req.black_piece_squares) - yp = list(req.black_piece_types) - else: - xs = list(req.black_piece_squares) - xp = list(req.black_piece_types) - ys = list(req.white_piece_squares) - yp = list(req.white_piece_types) - - # Captured pawn trick: from ep square to captured. - xed = req.epsq ^ (1 << 3) - - # Find captured index (j). - try: - j = ys.index(xed) - except ValueError: - j = -1 - - # Try first possible ep capture. - if 0 == (0x88 & (map88(xed) + 1)): - capturer_a = xed + 1 - - # Try second possible ep capture. - if 0 == (0x88 & (map88(xed) - 1)): - capturer_b = xed - 1 - - if (j > -1) and (ys[j] == xed): - # Find capturers (i). - for i in range(len(xs)): - if xp[i] == chess.PAWN and (xs[i] == capturer_a or xs[i] == capturer_b): - epscore = iFORBID - - # Copy position. - xs_after = xs[:] - ys_after = ys[:] - xp_after = xp[:] - yp_after = yp[:] - - # Execute capture. - xs_after[i] = req.epsq - removepiece(ys_after, yp_after, j) - - # Flip back. - if req.side == 1: - xs_after, ys_after = ys_after, xs_after - xp_after, yp_after = yp_after, xp_after - - # Make subrequest. - subreq = Request(xs_after, xp_after, ys_after, yp_after, opp(req.side), NOSQUARE) - try: - epscore = self._tb_probe(subreq) - epscore = adjust_up(epscore) - - # Choose to ep or not. - dtm = bestx(req.side, epscore, dtm) - except IndexError: - break - - return dtm - def egtb_block_getnumber(self, req: Request, idx: int) -> int: maxindex = EGKEY[req.egkey].maxindex diff --git a/test.py b/test.py index 6db84d254..7a3a3cdef 100755 --- a/test.py +++ b/test.py @@ -4348,6 +4348,11 @@ def test_two_ep(self): board = chess.Board("K7/8/8/6k1/5pPp/8/8/8 b - g3 0 61") self.assertEqual(self.tablebase.probe_dtm(board), 17) + @catchAndSkip(chess.gaviota.MissingTableError, "need KPvKP.gtb.cp4") + def test_ep_is_best(self): + board = chess.Board("8/8/7k/8/1pP5/7K/8/8 b - c3 0 1") + self.assertEqual(self.tablebase.probe_dtm(board), 19) + class SvgTestCase(unittest.TestCase): From 06de70e2e87969743dfa2196db1e2cbe687a08a8 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Mon, 24 Feb 2025 21:52:23 +0100 Subject: [PATCH 092/116] Fix checkmating ep capture in chess.gaviota.PythonTablebase --- chess/gaviota.py | 17 ++++++++++------- test.py | 10 ++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/chess/gaviota.py b/chess/gaviota.py index 83fb7bb64..281df836b 100644 --- a/chess/gaviota.py +++ b/chess/gaviota.py @@ -1493,8 +1493,8 @@ def probe_dtm(self, board: chess.Board) -> int: Probes for depth to mate information. The absolute value is the number of half-moves until forced mate - (or ``0`` in drawn positions). The value is positive if the - side to move is winning, otherwise it is negative. + (or ``0`` in drawn or checkmated positions). The value is positive if + the side to move is winning, otherwise it is negative or 0. In the example position, white to move will get mated in 10 half-moves: @@ -1532,11 +1532,14 @@ def probe_dtm(self, board: chess.Board) -> int: try: board.push(move) - child_dtm = -self._probe_dtm_no_ep(board) - if child_dtm > 0: - child_dtm += 1 - elif child_dtm < 0: - child_dtm -= 1 + if board.is_checkmate(): + child_dtm = 1 + else: + child_dtm = -self._probe_dtm_no_ep(board) + if child_dtm > 0: + child_dtm += 1 + elif child_dtm < 0: + child_dtm -= 1 dtm = min(dtm, child_dtm) if dtm * child_dtm > 0 else max(dtm, child_dtm) finally: diff --git a/test.py b/test.py index 7a3a3cdef..c120c049f 100755 --- a/test.py +++ b/test.py @@ -4353,6 +4353,16 @@ def test_ep_is_best(self): board = chess.Board("8/8/7k/8/1pP5/7K/8/8 b - c3 0 1") self.assertEqual(self.tablebase.probe_dtm(board), 19) + @catchAndSkip(chess.gaviota.MissingTableError, "need KQPvKP.gtb.cp4") + def test_ep_is_mate(self): + # The resulting mate. + board = chess.Board("5Q2/7k/6P1/5K2/8/8/8/8 b - - 0 1") + self.assertEqual(self.tablebase.probe_dtm(board), 0) + + # Ep leads to the previously tested mate position. + board = chess.Board("5Q2/7k/8/5KpP/8/8/8/8 w - g6 0 1") + self.assertEqual(self.tablebase.probe_dtm(board), 1) + class SvgTestCase(unittest.TestCase): From 45f616fae51a2b08ca4b4d0c01ddddf175d81ff9 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Tue, 25 Feb 2025 20:05:16 +0100 Subject: [PATCH 093/116] Supprt Python 3.13 --- .github/workflows/test.yml | 8 ++++---- setup.py | 1 + tox.ini | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 035b7f3d1..22d3b278a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest] - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: - python-version: "3.12" + python-version: "3.13" - run: pip install -e . - run: python examples/perft/perft.py -t 1 examples/perft/random.perft --max-nodes 10000 - run: python examples/perft/perft.py -t 1 examples/perft/chess960.perft --max-nodes 100000 @@ -39,7 +39,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest] - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -59,7 +59,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: - python-version: "3.12" + python-version: "3.13" - run: sudo apt-get update && sudo apt-get install -y docutils-common - run: python setup.py --long-description | rst2html --strict --no-raw > /dev/null - run: pip install -e . diff --git a/setup.py b/setup.py index b825adb44..9d6aa8c58 100755 --- a/setup.py +++ b/setup.py @@ -83,6 +83,7 @@ def read_description(): "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Games/Entertainment :: Board Games", "Topic :: Games/Entertainment :: Turn Based Strategy", "Topic :: Software Development :: Libraries :: Python Modules", diff --git a/tox.ini b/tox.ini index 8d8033676..5970b2c79 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py38,py39,py310,py311 +envlist = py38,py39,py310,py311,py312,py313 [testenv] passenv = LD_LIBRARY_PATH From b3c1f62c82b5fc40b14fa33bc9edd31cef68a944 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Tue, 25 Feb 2025 19:20:27 +0100 Subject: [PATCH 094/116] Prepare 1.11.2 --- CHANGELOG.rst | 8 ++++++++ chess/__init__.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 05b899a20..03a9555d1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,14 @@ Changelog for python-chess ========================== +New in v1.11.2 (25th Feb 2025) +------------------------------ + +Bugfixes: + +* Fix ``chess.gaviota.PythonTablebase`` does not properly resolve positions + where en passant captures are the best move. + New in v1.11.1 (9th Oct 2024) ----------------------------- diff --git a/chess/__init__.py b/chess/__init__.py index 8d0a68258..268f91cd5 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -11,7 +11,7 @@ __email__ = "niklas.fiekas@backscattering.de" -__version__ = "1.11.1" +__version__ = "1.11.2" import collections import copy From 2b8b0eb06accff3c4dea2e1cec108b3900a4f1d5 Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Mon, 24 Mar 2025 01:54:37 -0700 Subject: [PATCH 095/116] Add some missing docstrings This should cause these methods to appear in the readthedocs docs. --- chess/__init__.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/chess/__init__.py b/chess/__init__.py index 268f91cd5..28bcad0cd 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -1810,11 +1810,13 @@ def ply(self) -> int: return 2 * (self.fullmove_number - 1) + (self.turn == BLACK) def remove_piece_at(self, square: Square) -> Optional[Piece]: + """Remove a piece, if any, from the given square and return the removed piece.""" piece = super().remove_piece_at(square) self.clear_stack() return piece def set_piece_at(self, square: Square, piece: Optional[Piece], promoted: bool = False) -> None: + """Place a piece on a square.""" super().set_piece_at(square, piece, promoted=promoted) self.clear_stack() @@ -1998,6 +2000,7 @@ def is_pseudo_legal(self, move: Move) -> bool: return bool(self.attacks_mask(move.from_square) & to_mask) def is_legal(self, move: Move) -> bool: + """Check if a move is legal in the current position.""" return not self.is_variant_end() and self.is_pseudo_legal(move) and not self.is_into_check(move) def is_variant_end(self) -> bool: @@ -2034,9 +2037,27 @@ def is_variant_draw(self) -> bool: return False def is_game_over(self, *, claim_draw: bool = False) -> bool: + """ + Check if the game is over by any rule. + + The game is not considered to be over by the + :func:`fifty-move rule ` or + :func:`threefold repetition `, + unless *claim_draw* is given. Note that checking the latter can be + slow. + """ return self.outcome(claim_draw=claim_draw) is not None def result(self, *, claim_draw: bool = False) -> str: + """ + Return the result of a game: 1-0, 0-1, 1/2-1/2, or *. + + The game is not considered to be over by the + :func:`fifty-move rule ` or + :func:`threefold repetition `, + unless *claim_draw* is given. Note that checking the latter can be + slow. + """ outcome = self.outcome(claim_draw=claim_draw) return outcome.result() if outcome else "*" From b2144c2564f740ea120d037e9ec6129d8a3bea2c Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 3 Oct 2025 23:01:01 +0200 Subject: [PATCH 096/116] Remove explicit CodeQL configuration --- .github/workflows/codeql.yml | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index bc991c885..000000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: CodeQL - -on: [push, pull_request] - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - steps: - - uses: actions/checkout@v4 - - uses: github/codeql-action/init@v3 - with: - languages: python - - uses: github/codeql-action/analyze@v3 From 760360b8ddb65129aea46f84d99b5491e6ed6435 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 3 Oct 2025 23:04:49 +0200 Subject: [PATCH 097/116] Explicitly specify CI workflow permissions --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 22d3b278a..9eca0fce3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,6 +2,9 @@ name: Test on: [push, pull_request] +permissions: + contents: read + jobs: test: strategy: From 6b1cfedd442a05767ee28c7752a800ad4190f423 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 3 Oct 2025 23:14:56 +0200 Subject: [PATCH 098/116] Fix chess.gaviota bytearray usage does not pass mypy 1.18 --- chess/gaviota.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chess/gaviota.py b/chess/gaviota.py index 281df836b..dc19557cd 100644 --- a/chess/gaviota.py +++ b/chess/gaviota.py @@ -1716,7 +1716,7 @@ def _tb_probe(self, req: Request) -> int: z = self.egtb_block_getsize_zipped(req.egkey, block) self.egtb_block_park(req.egkey, block, stream) - buffer_zipped = stream.read(z) + buffer_zipped: bytearray | bytes = stream.read(z) if buffer_zipped[0] == 0: # If flag is zero, plain LZMA is following. From 71f5a21fe9a83770081fa9f7d2deb3835db984e4 Mon Sep 17 00:00:00 2001 From: Jackson Hall Date: Sat, 4 Oct 2025 16:52:41 -0400 Subject: [PATCH 099/116] Add and use rank/file constants --- chess/__init__.py | 80 +++++++++++++++++++++++++++++------------------ chess/gaviota.py | 8 ++--- chess/syzygy.py | 2 +- 3 files changed, 55 insertions(+), 35 deletions(-) diff --git a/chess/__init__.py b/chess/__init__.py index 28bcad0cd..3d3dad4f0 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -64,8 +64,28 @@ def piece_name(piece_type: PieceType) -> str: "P": "♙", "p": "♟", } +File: TypeAlias = int +FILE_A: File = 0 +FILE_B: File = 1 +FILE_C: File = 2 +FILE_D: File = 3 +FILE_E: File = 4 +FILE_F: File = 5 +FILE_G: File = 6 +FILE_H: File = 7 +FILES = [FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H] FILE_NAMES = ["a", "b", "c", "d", "e", "f", "g", "h"] +Rank: TypeAlias = int +RANK_1: Rank = 0 +RANK_2: Rank = 1 +RANK_3: Rank = 2 +RANK_4: Rank = 3 +RANK_5: Rank = 4 +RANK_6: Rank = 5 +RANK_7: Rank = 6 +RANK_8: Rank = 7 +RANKS = [RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8] RANK_NAMES = ["1", "2", "3", "4", "5", "6", "7", "8"] STARTING_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" @@ -251,15 +271,15 @@ def square_name(square: Square) -> str: """Gets the name of the square, like ``a3``.""" return SQUARE_NAMES[square] -def square(file_index: int, rank_index: int) -> Square: +def square(file_index: File, rank_index: Rank) -> Square: """Gets a square number by file and rank index.""" return rank_index * 8 + file_index -def square_file(square: Square) -> int: +def square_file(square: Square) -> File: """Gets the file index of the square where ``0`` is the a-file.""" return square & 7 -def square_rank(square: Square) -> int: +def square_rank(square: Square) -> Rank: """Gets the rank index of the square where ``0`` is the first rank.""" return square >> 3 @@ -376,24 +396,24 @@ def square_mirror(square: Square) -> Square: BB_LIGHT_SQUARES: Bitboard = 0x55aa_55aa_55aa_55aa BB_DARK_SQUARES: Bitboard = 0xaa55_aa55_aa55_aa55 -BB_FILE_A: Bitboard = 0x0101_0101_0101_0101 << 0 -BB_FILE_B: Bitboard = 0x0101_0101_0101_0101 << 1 -BB_FILE_C: Bitboard = 0x0101_0101_0101_0101 << 2 -BB_FILE_D: Bitboard = 0x0101_0101_0101_0101 << 3 -BB_FILE_E: Bitboard = 0x0101_0101_0101_0101 << 4 -BB_FILE_F: Bitboard = 0x0101_0101_0101_0101 << 5 -BB_FILE_G: Bitboard = 0x0101_0101_0101_0101 << 6 -BB_FILE_H: Bitboard = 0x0101_0101_0101_0101 << 7 +BB_FILE_A: Bitboard = 0x0101_0101_0101_0101 << FILE_A +BB_FILE_B: Bitboard = 0x0101_0101_0101_0101 << FILE_B +BB_FILE_C: Bitboard = 0x0101_0101_0101_0101 << FILE_C +BB_FILE_D: Bitboard = 0x0101_0101_0101_0101 << FILE_D +BB_FILE_E: Bitboard = 0x0101_0101_0101_0101 << FILE_E +BB_FILE_F: Bitboard = 0x0101_0101_0101_0101 << FILE_F +BB_FILE_G: Bitboard = 0x0101_0101_0101_0101 << FILE_G +BB_FILE_H: Bitboard = 0x0101_0101_0101_0101 << FILE_H BB_FILES: List[Bitboard] = [BB_FILE_A, BB_FILE_B, BB_FILE_C, BB_FILE_D, BB_FILE_E, BB_FILE_F, BB_FILE_G, BB_FILE_H] -BB_RANK_1: Bitboard = 0xff << (8 * 0) -BB_RANK_2: Bitboard = 0xff << (8 * 1) -BB_RANK_3: Bitboard = 0xff << (8 * 2) -BB_RANK_4: Bitboard = 0xff << (8 * 3) -BB_RANK_5: Bitboard = 0xff << (8 * 4) -BB_RANK_6: Bitboard = 0xff << (8 * 5) -BB_RANK_7: Bitboard = 0xff << (8 * 6) -BB_RANK_8: Bitboard = 0xff << (8 * 7) +BB_RANK_1: Bitboard = 0xff << (8 * RANK_1) +BB_RANK_2: Bitboard = 0xff << (8 * RANK_2) +BB_RANK_3: Bitboard = 0xff << (8 * RANK_3) +BB_RANK_4: Bitboard = 0xff << (8 * RANK_4) +BB_RANK_5: Bitboard = 0xff << (8 * RANK_5) +BB_RANK_6: Bitboard = 0xff << (8 * RANK_6) +BB_RANK_7: Bitboard = 0xff << (8 * RANK_7) +BB_RANK_8: Bitboard = 0xff << (8 * RANK_8) BB_RANKS: List[Bitboard] = [BB_RANK_1, BB_RANK_2, BB_RANK_3, BB_RANK_4, BB_RANK_5, BB_RANK_6, BB_RANK_7, BB_RANK_8] BB_BACKRANKS: Bitboard = BB_RANK_1 | BB_RANK_8 @@ -1847,7 +1867,7 @@ def generate_pseudo_legal_moves(self, from_mask: Bitboard = BB_ALL, to_mask: Bit self.occupied_co[not self.turn] & to_mask) for to_square in scan_reversed(targets): - if square_rank(to_square) in [0, 7]: + if square_rank(to_square) in [RANK_1, RANK_8]: yield Move(from_square, to_square, QUEEN) yield Move(from_square, to_square, ROOK) yield Move(from_square, to_square, BISHOP) @@ -1870,7 +1890,7 @@ def generate_pseudo_legal_moves(self, from_mask: Bitboard = BB_ALL, to_mask: Bit for to_square in scan_reversed(single_moves): from_square = to_square + (8 if self.turn == BLACK else -8) - if square_rank(to_square) in [0, 7]: + if square_rank(to_square) in [RANK_1, RANK_8]: yield Move(from_square, to_square, QUEEN) yield Move(from_square, to_square, ROOK) yield Move(from_square, to_square, BISHOP) @@ -1897,7 +1917,7 @@ def generate_pseudo_legal_ep(self, from_mask: Bitboard = BB_ALL, to_mask: Bitboa capturers = ( self.pawns & self.occupied_co[self.turn] & from_mask & BB_PAWN_ATTACKS[not self.turn][self.ep_square] & - BB_RANKS[4 if self.turn else 3]) + BB_RANKS[RANK_5 if self.turn else RANK_4]) for capturer in scan_reversed(capturers): yield Move(capturer, self.ep_square) @@ -1977,9 +1997,9 @@ def is_pseudo_legal(self, move: Move) -> bool: if piece != PAWN: return False - if self.turn == WHITE and square_rank(move.to_square) != 7: + if self.turn == WHITE and square_rank(move.to_square) != RANK_8: return False - elif self.turn == BLACK and square_rank(move.to_square) != 0: + elif self.turn == BLACK and square_rank(move.to_square) != RANK_1: return False # Handle castling. @@ -2401,18 +2421,18 @@ def push(self, move: Move) -> None: else: self.castling_rights &= ~BB_RANK_8 elif captured_piece_type == KING and not self.promoted & to_bb: - if self.turn == WHITE and square_rank(move.to_square) == 7: + if self.turn == WHITE and square_rank(move.to_square) == RANK_8: self.castling_rights &= ~BB_RANK_8 - elif self.turn == BLACK and square_rank(move.to_square) == 0: + elif self.turn == BLACK and square_rank(move.to_square) == RANK_1: self.castling_rights &= ~BB_RANK_1 # Handle special pawn moves. if piece_type == PAWN: diff = move.to_square - move.from_square - if diff == 16 and square_rank(move.from_square) == 1: + if diff == 16 and square_rank(move.from_square) == RANK_2: self.ep_square = move.from_square + 8 - elif diff == -16 and square_rank(move.from_square) == 6: + elif diff == -16 and square_rank(move.from_square) == RANK_7: self.ep_square = move.from_square - 8 elif move.to_square == ep_square and abs(diff) in [7, 9] and not captured_piece_type: # Remove pawns captured en passant. @@ -3605,11 +3625,11 @@ def _valid_ep_square(self) -> Optional[Square]: return None if self.turn == WHITE: - ep_rank = 5 + ep_rank = RANK_6 pawn_mask = shift_down(BB_SQUARES[self.ep_square]) seventh_rank_mask = shift_up(BB_SQUARES[self.ep_square]) else: - ep_rank = 2 + ep_rank = RANK_3 pawn_mask = shift_up(BB_SQUARES[self.ep_square]) seventh_rank_mask = shift_down(BB_SQUARES[self.ep_square]) diff --git a/chess/gaviota.py b/chess/gaviota.py index dc19557cd..c352a27ca 100644 --- a/chess/gaviota.py +++ b/chess/gaviota.py @@ -110,12 +110,12 @@ def idx_is_empty(x: int) -> int: def flip_type(x: chess.Square, y: chess.Square) -> int: ret = 0 - if chess.square_file(x) > 3: + if chess.square_file(x) > chess.FILE_D: x = flip_we(x) y = flip_we(y) ret |= 1 - if chess.square_rank(x) > 3: + if chess.square_rank(x) > chess.RANK_4: x = flip_ns(x) y = flip_ns(y) ret |= 2 @@ -351,11 +351,11 @@ def init_ppidx() -> Tuple[List[List[int]], List[int], List[int]]: def norm_kkindex(x: chess.Square, y: chess.Square) -> Tuple[int, int]: - if chess.square_file(x) > 3: + if chess.square_file(x) > chess.FILE_D: x = flip_we(x) y = flip_we(y) - if chess.square_rank(x) > 3: + if chess.square_rank(x) > chess.RANK_4: x = flip_ns(x) y = flip_ns(y) diff --git a/chess/syzygy.py b/chess/syzygy.py index c61890550..0c6b7822c 100644 --- a/chess/syzygy.py +++ b/chess/syzygy.py @@ -761,7 +761,7 @@ def calc_symlen(self, d: PairsData, s: int, tmp: List[int]) -> None: d.symlen[s] = d.symlen[s1] + d.symlen[s2] + 1 tmp[s] = 1 - def pawn_file(self, pos: List[chess.Square]) -> int: + def pawn_file(self, pos: List[chess.Square]) -> chess.File: for i in range(1, self.pawns[0]): if FLAP[pos[0]] > FLAP[pos[i]]: pos[0], pos[i] = pos[i], pos[0] From bd8074d20e7aa667315b54470d0f3aae6390e69d Mon Sep 17 00:00:00 2001 From: Jackson Hall Date: Sat, 4 Oct 2025 20:47:45 -0400 Subject: [PATCH 100/116] Add `parse_file`/`parse_rank`, `file_name`/rank_name` --- chess/__init__.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/chess/__init__.py b/chess/__init__.py index 3d3dad4f0..84bfa632a 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -275,6 +275,32 @@ def square(file_index: File, rank_index: Rank) -> Square: """Gets a square number by file and rank index.""" return rank_index * 8 + file_index +def parse_file(name: str) -> File: + """ + Gets the file index for the given file *name* + (e.g., ``a`` returns ``0``). + + :raises: :exc:`ValueError` if the file name is invalid. + """ + return FILE_NAMES.index(name) + +def file_name(file: File) -> str: + """Gets the name of the file, like ``a``.""" + return FILE_NAMES[file] + +def parse_rank(name: str) -> File: + """ + Gets the rank index for the given rank *name* + (e.g., ``1`` returns ``0``). + + :raises: :exc:`ValueError` if the rank name is invalid. + """ + return FILE_NAMES.index(name) + +def rank_name(rank: Rank) -> str: + """Gets the name of the rank, like ``1``.""" + return FILE_NAMES[rank] + def square_file(square: Square) -> File: """Gets the file index of the square where ``0`` is the a-file.""" return square & 7 From 376d603694913a82bcb2efa594972a5fba5804f6 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 11 Oct 2025 09:20:54 +0200 Subject: [PATCH 101/116] Explicitly support Python 3.14 --- .github/workflows/test.yml | 24 ++++++++++++------------ setup.py | 1 + tox.ini | 2 +- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9eca0fce3..6b31bce24 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,11 +10,11 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest] - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - run: .github/workflows/setup-${{ matrix.os }}.sh @@ -24,10 +24,10 @@ jobs: perft: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 with: - python-version: "3.13" + python-version: "3.14" - run: pip install -e . - run: python examples/perft/perft.py -t 1 examples/perft/random.perft --max-nodes 10000 - run: python examples/perft/perft.py -t 1 examples/perft/chess960.perft --max-nodes 100000 @@ -42,11 +42,11 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - run: pip install -e . @@ -59,10 +59,10 @@ jobs: readme: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 with: - python-version: "3.13" + python-version: "3.14" - run: sudo apt-get update && sudo apt-get install -y docutils-common - run: python setup.py --long-description | rst2html --strict --no-raw > /dev/null - run: pip install -e . diff --git a/setup.py b/setup.py index 9d6aa8c58..5815947cd 100755 --- a/setup.py +++ b/setup.py @@ -84,6 +84,7 @@ def read_description(): "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Games/Entertainment :: Board Games", "Topic :: Games/Entertainment :: Turn Based Strategy", "Topic :: Software Development :: Libraries :: Python Modules", diff --git a/tox.ini b/tox.ini index 5970b2c79..493a46feb 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py38,py39,py310,py311,py312,py313 +envlist = py38,py39,py310,py311,py312,py313,py314 [testenv] passenv = LD_LIBRARY_PATH From e4386c2f1efcb686c1d6222681cc84d1f0b06ded Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 11 Oct 2025 15:25:33 +0200 Subject: [PATCH 102/116] Remove chess.engine.DefaultEventLoopPolicy (breaking change forced by Python) --- chess/engine.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/chess/engine.py b/chess/engine.py index b979b278f..8482c31ba 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -50,10 +50,6 @@ def override(fn: F, /) -> F: MANAGED_OPTIONS = ["uci_chess960", "uci_variant", "multipv", "ponder"] -# No longer needed, but alias kept around for compatibility. -EventLoopPolicy = asyncio.DefaultEventLoopPolicy - - def run_in_background(coroutine: Callable[[concurrent.futures.Future[T]], Coroutine[Any, Any, None]], *, name: Optional[str] = None, debug: Optional[bool] = None) -> T: """ Runs ``coroutine(future)`` in a new event loop on a background thread. From e974a37e52a59709a0988872a12c1f01244a8c15 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 11 Oct 2025 15:30:39 +0200 Subject: [PATCH 103/116] Fix test_sf_forced_mates() failing due to ambiguous mate --- test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.py b/test.py index c120c049f..2ebd357d0 100755 --- a/test.py +++ b/test.py @@ -3079,7 +3079,7 @@ def test_sf_forced_mates(self): for epd in epds: operations = board.set_epd(epd) - result = engine.play(board, chess.engine.Limit(mate=5), game=object()) + result = engine.play(board, chess.engine.Limit(mate=3), game=object()) self.assertIn(result.move, operations["bm"], operations["id"]) @catchAndSkip(FileNotFoundError, "need stockfish") From 8412bd56a282f7fe7071a8b1788b6b791d5e7b0e Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 11 Oct 2025 15:33:30 +0200 Subject: [PATCH 104/116] asyncio.iscoroutinefunction() -> inspect.iscoroutinefunction() --- chess/engine.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chess/engine.py b/chess/engine.py index 8482c31ba..913940190 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -8,6 +8,7 @@ import copy import dataclasses import enum +import inspect import logging import math import shlex @@ -58,7 +59,7 @@ def run_in_background(coroutine: Callable[[concurrent.futures.Future[T]], Corout The coroutine and all remaining tasks continue running in the background until complete. """ - assert asyncio.iscoroutinefunction(coroutine) + assert inspect.iscoroutinefunction(coroutine) future: concurrent.futures.Future[T] = concurrent.futures.Future() From 624d3a730c180e749ea04a473a828b9c31ff52a4 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 11 Oct 2025 15:38:19 +0200 Subject: [PATCH 105/116] Do not fail-fast matrix jobs that may have interesting results --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6b31bce24..9d0880bb4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,6 +11,7 @@ jobs: matrix: os: [ubuntu-latest, windows-latest] python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + fail-fast: false runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v5 @@ -43,6 +44,7 @@ jobs: matrix: os: [ubuntu-latest, windows-latest] python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + fail-fast: false runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 From 4d9b3bfd860bfa95731d4e208fd98c7c10a15533 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Mon, 27 Oct 2025 21:24:16 +0100 Subject: [PATCH 106/116] Fix Gaviota tables opened as writable (fixes #1166) --- chess/gaviota.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chess/gaviota.py b/chess/gaviota.py index c352a27ca..8beb18d4d 100644 --- a/chess/gaviota.py +++ b/chess/gaviota.py @@ -1663,7 +1663,7 @@ def _open_tablebase(self, req: Request) -> BinaryIO: if stream is None: path = self.available_tables[req.egkey] - stream = open(path, "rb+") + stream = open(path, "rb") self.egtb_loadindexes(req.egkey, stream) self.streams[req.egkey] = stream From f8575f962caf3fcddcf95d935d1d75a1cceb586b Mon Sep 17 00:00:00 2001 From: Jackson Hall Date: Sun, 2 Nov 2025 18:46:03 -0500 Subject: [PATCH 107/116] Add `Board.gives_checkmate()` --- chess/__init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/chess/__init__.py b/chess/__init__.py index 84bfa632a..b51c31ca3 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -1980,6 +1980,17 @@ def gives_check(self, move: Move) -> bool: finally: self.pop() + def gives_checkmate(self, move: Move) -> bool: + """ + Probes if the given move would put the opponent in checkmate. The move + must be at least pseudo-legal. + """ + self.push(move) + try: + return self.is_checkmate() + finally: + self.pop() + def is_into_check(self, move: Move) -> bool: king = self.king(self.turn) if king is None: From d59bad55df4b8759e53b0ee8673ec8e69c5f5a82 Mon Sep 17 00:00:00 2001 From: johndoknjas Date: Fri, 2 Jan 2026 10:02:39 -0800 Subject: [PATCH 108/116] Ensure hash is always set after threads. --- chess/engine.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/chess/engine.py b/chess/engine.py index 913940190..72b579dc9 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -1875,12 +1875,16 @@ def _parse_uci_bestmove(board: chess.Board, args: str) -> BestMove: return BestMove(move, ponder) -def _chain_config(a: ConfigMapping, b: ConfigMapping) -> Iterator[Tuple[str, ConfigValue]]: - for name, value in a.items(): +def _chain_config(a: ConfigMapping, b: ConfigMapping, with_hash_reordering: bool = True) -> Iterator[Tuple[str, ConfigValue]]: + merged = dict(a) + for k, v in b.items(): + merged.setdefault(k, v) + if with_hash_reordering and 'Hash' in merged and 'Threads' in merged: + hash_val = merged['Hash'] + del merged['Hash'] + merged['Hash'] = hash_val + for name, value in merged.items(): yield name, value - for name, value in b.items(): - if name not in a: - yield name, value class UciOptionMap(MutableMapping[str, T]): From a5bbe3ea49f04b6a153efd278d1d17b073a11fc0 Mon Sep 17 00:00:00 2001 From: johndoknjas Date: Fri, 2 Jan 2026 20:46:19 -0800 Subject: [PATCH 109/116] Remove optional parameter. --- chess/engine.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chess/engine.py b/chess/engine.py index 72b579dc9..0a9af75d9 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -1875,11 +1875,11 @@ def _parse_uci_bestmove(board: chess.Board, args: str) -> BestMove: return BestMove(move, ponder) -def _chain_config(a: ConfigMapping, b: ConfigMapping, with_hash_reordering: bool = True) -> Iterator[Tuple[str, ConfigValue]]: +def _chain_config(a: ConfigMapping, b: ConfigMapping) -> Iterator[Tuple[str, ConfigValue]]: merged = dict(a) for k, v in b.items(): merged.setdefault(k, v) - if with_hash_reordering and 'Hash' in merged and 'Threads' in merged: + if 'Hash' in merged and 'Threads' in merged: hash_val = merged['Hash'] del merged['Hash'] merged['Hash'] = hash_val From a28315bbfe31410120e97aa2f2fe56a19043e242 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 3 Jan 2026 12:29:26 +0100 Subject: [PATCH 110/116] Comment Hash after Threads --- chess/engine.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/chess/engine.py b/chess/engine.py index 0a9af75d9..c66bc0c45 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -1875,16 +1875,16 @@ def _parse_uci_bestmove(board: chess.Board, args: str) -> BestMove: return BestMove(move, ponder) -def _chain_config(a: ConfigMapping, b: ConfigMapping) -> Iterator[Tuple[str, ConfigValue]]: +def _chain_config(a: ConfigMapping, b: ConfigMapping) -> Iterable[Tuple[str, ConfigValue]]: merged = dict(a) for k, v in b.items(): merged.setdefault(k, v) - if 'Hash' in merged and 'Threads' in merged: - hash_val = merged['Hash'] - del merged['Hash'] - merged['Hash'] = hash_val - for name, value in merged.items(): - yield name, value + if "Hash" in merged and "Threads" in merged: + # Move Hash after Threads, as recommended by Stockfish. + hash_val = merged["Hash"] + del merged["Hash"] + merged["Hash"] = hash_val + return merged.items() class UciOptionMap(MutableMapping[str, T]): From 76cbe9843b7be94676cf19ea2a446e4eb3ac4291 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Fri, 13 Feb 2026 21:41:52 +0000 Subject: [PATCH 111/116] Multiple kings per color (was_into_check and more affected) (#1179) * fixed multiple kings for was_into_check * fixed king() behavior on multiple kings minus prev commit * add test cases (very little) * using the precomputed king mask * removed testcase of was_into_check() on multiple kings... ... because it would cause the function to return False (because king() didn't detect any king because of decision) --- chess/__init__.py | 2 +- test.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/chess/__init__.py b/chess/__init__.py index b51c31ca3..347f22ea1 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -916,7 +916,7 @@ def king(self, color: Color) -> Optional[Square]: considered. """ king_mask = self.occupied_co[color] & self.kings & ~self.promoted - return msb(king_mask) if king_mask else None + return msb(king_mask) if king_mask and popcount(king_mask) == 1 else None def attacks_mask(self, square: Square) -> Bitboard: bb_square = BB_SQUARES[square] diff --git a/test.py b/test.py index 2ebd357d0..4927a2b80 100755 --- a/test.py +++ b/test.py @@ -1720,6 +1720,10 @@ def test_impossible_check_due_to_en_passant(self): self.assertFalse(board.has_legal_en_passant()) self.assertEqual(len(list(board.legal_moves)), 2) + def test_multiple_kings(self): + board = chess.Board("KKKK1kkk/8/8/8/8/8/8/8 w - - 0 1") + self.assertEqual(board.king(chess.WHITE), None) + class LegalMoveGeneratorTestCase(unittest.TestCase): From 312f3bf07758628e4ee9befbd9e3df7dd5eccea6 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 13 Feb 2026 22:52:23 +0100 Subject: [PATCH 112/116] Introduce Board._effective_promoted() --- chess/__init__.py | 55 ++++++++++++++++++++++++++++------------------- chess/variant.py | 27 +++++++---------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/chess/__init__.py b/chess/__init__.py index 347f22ea1..7fe4cb9cc 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -907,16 +907,19 @@ def color_at(self, square: Square) -> Optional[Color]: else: return None + def _effective_promoted(self) -> Bitboard: + return BB_EMPTY + def king(self, color: Color) -> Optional[Square]: """ - Finds the king square of the given side. Returns ``None`` if there - is no king of that color. + Finds the unique king square of the given side. Returns ``None`` if + there is no king or multiple kings of that color. In variants with king promotions, only non-promoted kings are considered. """ - king_mask = self.occupied_co[color] & self.kings & ~self.promoted - return msb(king_mask) if king_mask and popcount(king_mask) == 1 else None + king_mask = self.occupied_co[color] & self.kings & ~self._effective_promoted() + return msb(king_mask) if king_mask and not king_mask & (king_mask - 1) else None def attacks_mask(self, square: Square) -> Bitboard: bb_square = BB_SQUARES[square] @@ -1135,7 +1138,7 @@ def set_piece_at(self, square: Square, piece: Optional[Piece], promoted: bool = else: self._set_piece_at(square, piece.piece_type, piece.color, promoted) - def board_fen(self, *, promoted: Optional[bool] = False) -> str: + def board_fen(self, *, promoted: Optional[bool] = None) -> str: """ Gets the board FEN (e.g., ``rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR``). @@ -1153,7 +1156,14 @@ def board_fen(self, *, promoted: Optional[bool] = False) -> str: builder.append(str(empty)) empty = 0 builder.append(piece.symbol()) - if promoted and BB_SQUARES[square] & self.promoted: + + if promoted is None: + promoted_mask = self._effective_promoted() + elif promoted: + promoted_mask = self.promoted + else: + promoted_mask = BB_EMPTY + if BB_SQUARES[square] & promoted_mask: builder.append("~") if BB_SQUARES[square] & BB_FILE_H: @@ -1335,7 +1345,7 @@ def chess960_pos(self) -> Optional[int]: return None if self.pawns != BB_RANK_2 | BB_RANK_7: return None - if self.promoted: + if self._effective_promoted(): return None # Piece counts. @@ -2452,12 +2462,12 @@ def push(self, move: Move) -> None: # Update castling rights. self.castling_rights &= ~to_bb & ~from_bb - if piece_type == KING and not promoted: + if piece_type == KING and not self._effective_promoted() & from_bb: if self.turn == WHITE: self.castling_rights &= ~BB_RANK_1 else: self.castling_rights &= ~BB_RANK_8 - elif captured_piece_type == KING and not self.promoted & to_bb: + elif captured_piece_type == KING and not self._effective_promoted() & to_bb: if self.turn == WHITE and square_rank(move.to_square) == RANK_8: self.castling_rights &= ~BB_RANK_8 elif self.turn == BLACK and square_rank(move.to_square) == RANK_1: @@ -3404,8 +3414,8 @@ def _reduces_castling_rights(self, move: Move) -> bool: cr = self.clean_castling_rights() touched = BB_SQUARES[move.from_square] ^ BB_SQUARES[move.to_square] return bool(touched & cr or - cr & BB_RANK_1 and touched & self.kings & self.occupied_co[WHITE] & ~self.promoted or - cr & BB_RANK_8 and touched & self.kings & self.occupied_co[BLACK] & ~self.promoted) + cr & BB_RANK_1 and touched & self.kings & self.occupied_co[WHITE] & ~self._effective_promoted() or + cr & BB_RANK_8 and touched & self.kings & self.occupied_co[BLACK] & ~self._effective_promoted()) def is_irreversible(self, move: Move) -> bool: """ @@ -3459,16 +3469,16 @@ def clean_castling_rights(self) -> Bitboard: black_castling &= (BB_A8 | BB_H8) # The kings must be on e1 or e8. - if not self.occupied_co[WHITE] & self.kings & ~self.promoted & BB_E1: + if not self.occupied_co[WHITE] & self.kings & ~self._effective_promoted() & BB_E1: white_castling = 0 - if not self.occupied_co[BLACK] & self.kings & ~self.promoted & BB_E8: + if not self.occupied_co[BLACK] & self.kings & ~self._effective_promoted() & BB_E8: black_castling = 0 return white_castling | black_castling else: # The kings must be on the back rank. - white_king_mask = self.occupied_co[WHITE] & self.kings & BB_RANK_1 & ~self.promoted - black_king_mask = self.occupied_co[BLACK] & self.kings & BB_RANK_8 & ~self.promoted + white_king_mask = self.occupied_co[WHITE] & self.kings & BB_RANK_1 & ~self._effective_promoted() + black_king_mask = self.occupied_co[BLACK] & self.kings & BB_RANK_8 & ~self._effective_promoted() if not white_king_mask: white_castling = 0 if not black_king_mask: @@ -3506,7 +3516,7 @@ def has_kingside_castling_rights(self, color: Color) -> bool: castling rights. """ backrank = BB_RANK_1 if color == WHITE else BB_RANK_8 - king_mask = self.kings & self.occupied_co[color] & backrank & ~self.promoted + king_mask = self.kings & self.occupied_co[color] & backrank & ~self._effective_promoted() if not king_mask: return False @@ -3527,7 +3537,7 @@ def has_queenside_castling_rights(self, color: Color) -> bool: castling rights. """ backrank = BB_RANK_1 if color == WHITE else BB_RANK_8 - king_mask = self.kings & self.occupied_co[color] & backrank & ~self.promoted + king_mask = self.kings & self.occupied_co[color] & backrank & ~self._effective_promoted() if not king_mask: return False @@ -3600,11 +3610,11 @@ def status(self) -> Status: errors |= STATUS_EMPTY # There must be exactly one king of each color. - if not self.occupied_co[WHITE] & self.kings: + if not self.occupied_co[WHITE] & self.kings & ~self._effective_promoted(): errors |= STATUS_NO_WHITE_KING - if not self.occupied_co[BLACK] & self.kings: + if not self.occupied_co[BLACK] & self.kings & ~self._effective_promoted(): errors |= STATUS_NO_BLACK_KING - if popcount(self.occupied & self.kings) > 2: + if popcount(self.occupied & self.kings & ~self._effective_promoted()) > 2: errors |= STATUS_TOO_MANY_KINGS # There can not be more than 16 pieces of any color. @@ -3638,7 +3648,7 @@ def status(self) -> Status: # More than the maximum number of possible checkers in the variant. checkers = self.checkers_mask() - our_kings = self.kings & self.occupied_co[self.turn] & ~self.promoted + our_kings = self.kings & self.occupied_co[self.turn] & ~self._effective_promoted() if checkers: if popcount(checkers) > 2: errors |= STATUS_TOO_MANY_CHECKERS @@ -3822,7 +3832,7 @@ def generate_castling_moves(self, from_mask: Bitboard = BB_ALL, to_mask: Bitboar return backrank = BB_RANK_1 if self.turn == WHITE else BB_RANK_8 - king = self.occupied_co[self.turn] & self.kings & ~self.promoted & backrank & from_mask + king = self.occupied_co[self.turn] & self.kings & ~self._effective_promoted() & backrank & from_mask king &= -king if not king: return @@ -3879,6 +3889,7 @@ def _to_chess960(self, move: Move) -> Move: def _transposition_key(self) -> Hashable: return (self.pawns, self.knights, self.bishops, self.rooks, self.queens, self.kings, + self._effective_promoted(), self.occupied_co[WHITE], self.occupied_co[BLACK], self.turn, self.clean_castling_rights(), self.ep_square if self.has_legal_en_passant() else None) diff --git a/chess/variant.py b/chess/variant.py index 6e9161dc8..ba4c0f1ce 100644 --- a/chess/variant.py +++ b/chess/variant.py @@ -127,16 +127,8 @@ def is_legal(self, move: chess.Move) -> bool: else: return not any(self.generate_pseudo_legal_captures()) - def _transposition_key(self) -> Hashable: - if self.has_chess960_castling_rights(): - return (super()._transposition_key(), self.kings & self.promoted) - else: - return super()._transposition_key() - - def board_fen(self, *, promoted: Optional[bool] = None) -> str: - if promoted is None: - promoted = self.has_chess960_castling_rights() - return super().board_fen(promoted=promoted) + def _effective_promoted(self) -> chess.Bitboard: + return self.kings & self.promoted if self.castling_rights else chess.BB_EMPTY def status(self) -> chess.Status: status = super().status() @@ -261,9 +253,9 @@ def _push_capture(self, move: chess.Move, capture_square: chess.Square, piece_ty # Destroy castling rights. self.castling_rights &= ~explosion_radius - if explosion_radius & self.kings & self.occupied_co[chess.WHITE] & ~self.promoted: + if explosion_radius & self.kings & self.occupied_co[chess.WHITE] & ~self._effective_promoted(): self.castling_rights &= ~chess.BB_RANK_1 - if explosion_radius & self.kings & self.occupied_co[chess.BLACK] & ~self.promoted: + if explosion_radius & self.kings & self.occupied_co[chess.BLACK] & ~self._effective_promoted(): self.castling_rights &= ~chess.BB_RANK_8 # Explode the capturing piece. @@ -930,9 +922,11 @@ def _is_halfmoves(self, n: int) -> bool: def is_irreversible(self, move: chess.Move) -> bool: return self._reduces_castling_rights(move) + def _effective_promoted(self) -> chess.Bitboard: + return self.promoted & ~self.kings & ~self.pawns + def _transposition_key(self) -> Hashable: return (super()._transposition_key(), - self.promoted, str(self.pockets[chess.WHITE]), str(self.pockets[chess.BLACK])) def legal_drop_squares_mask(self) -> chess.Bitboard: @@ -1009,7 +1003,7 @@ def has_insufficient_material(self, color: chess.Color) -> bool: # a different color complex. return ( chess.popcount(self.occupied) + sum(len(pocket) for pocket in self.pockets) <= 3 and - not self.promoted and + not self._effective_promoted() and not self.pawns and not self.rooks and not self.queens and @@ -1041,11 +1035,6 @@ def set_fen(self, fen: str) -> None: self.pockets[chess.WHITE] = white_pocket self.pockets[chess.BLACK] = black_pocket - def board_fen(self, *, promoted: Optional[bool] = None) -> str: - if promoted is None: - promoted = True - return super().board_fen(promoted=promoted) - def epd(self, shredder: bool = False, en_passant: chess.EnPassantSpec = "legal", promoted: Optional[bool] = None, **operations: Union[None, str, int, float, chess.Move, Iterable[chess.Move]]) -> str: epd = super().epd(shredder=shredder, en_passant=en_passant, promoted=promoted) board_part, info_part = epd.split(" ", 1) From fc50a27fa3cfa07243f78eca93ea6126347ea1fa Mon Sep 17 00:00:00 2001 From: Christopher Akiki Date: Fri, 13 Mar 2026 17:49:05 +0100 Subject: [PATCH 113/116] [MINOR:TYPO] Update pgn.py instanciate -> instantiate --- chess/pgn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chess/pgn.py b/chess/pgn.py index f40980d48..5ae5b43b0 100644 --- a/chess/pgn.py +++ b/chess/pgn.py @@ -402,7 +402,7 @@ def remove_variation(self, move: Union[int, chess.Move, GameNode]) -> None: def add_variation(self, move: chess.Move, *, comment: Union[str, list[str]] = "", starting_comment: Union[str, list[str]] = "", nags: Iterable[int] = []) -> ChildNode: """Creates a child node with the given attributes.""" - # Instanciate ChildNode only in this method. + # Instantiate ChildNode only in this method. return ChildNode(self, move, comment=comment, starting_comment=starting_comment, nags=nags) def add_main_variation(self, move: chess.Move, *, comment: str = "", nags: Iterable[int] = []) -> ChildNode: From 5e2a2bc153b7646497f3e811f3cfd28aaca1b1ea Mon Sep 17 00:00:00 2001 From: Cady Date: Mon, 30 Mar 2026 16:21:49 -0400 Subject: [PATCH 114/116] Fixed typo in README.rst, issue 1183 --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index a6c3185c6..d4d51a128 100644 --- a/README.rst +++ b/README.rst @@ -314,7 +314,7 @@ If you like, share interesting things you are using python-chess for, for exampl | .. image:: https://github.com/niklasf/python-chess/blob/master/docs/images/clente-chess.png?raw=true | `clente/chess `_ | | :height: 64 | | | :width: 64 | | -| :target: https://github.com/clente/chess | Oppinionated wrapper to use python-chess from the R programming language | +| :target: https://github.com/clente/chess | Opinionated wrapper to use python-chess from the R programming language | +------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------+ | .. image:: https://github.com/niklasf/python-chess/blob/master/docs/images/crazyara.png?raw=true | https://crazyara.org/ | | :height: 64 | | From efb8b4278c85e0145b29b867de03dc715456dd86 Mon Sep 17 00:00:00 2001 From: Litschi Date: Fri, 27 Mar 2026 19:31:11 +0100 Subject: [PATCH 115/116] feat: added piece_count function instead of chess.popcount(board.occupied) --- CHANGELOG.rst | 9 +++++++++ chess/__init__.py | 3 +++ chess/gaviota.py | 8 ++++---- chess/syzygy.py | 10 +++++----- test.py | 2 +- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 03a9555d1..212db89b1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,15 @@ Changelog for python-chess ========================== +New in unreleased (27th Mar 2026) +--------------------------------- + +Bugfixes: +* Fixed typo in README.rst. + +Changes: +* Added ``board.piece_count`` function. + New in v1.11.2 (25th Feb 2025) ------------------------------ diff --git a/chess/__init__.py b/chess/__init__.py index 7fe4cb9cc..12b249bd5 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -841,6 +841,9 @@ def clear_board(self) -> None: :class:`~chess.Board` also clears the move stack. """ self._clear_board() + + def piece_count(self) -> int: + return popcount(self.occupied) def pieces_mask(self, piece_type: PieceType, color: Color) -> Bitboard: if piece_type == PAWN: diff --git a/chess/gaviota.py b/chess/gaviota.py index 8beb18d4d..7152a18f0 100644 --- a/chess/gaviota.py +++ b/chess/gaviota.py @@ -1519,8 +1519,8 @@ def probe_dtm(self, board: chess.Board) -> int: raise KeyError(f"gaviota tables do not contain positions with castling rights: {board.fen()}") # Supports only up to 5 pieces. - if chess.popcount(board.occupied) > 5: - raise KeyError(f"gaviota tables support up to 5 pieces, not {chess.popcount(board.occupied)}: {board.fen()}") + if board.piece_count() > 5: + raise KeyError(f"gaviota tables support up to 5 pieces, not {board.piece_count()}: {board.fen()}") # KvK is a draw. if board.occupied == board.kings: @@ -1885,8 +1885,8 @@ def _probe_hard(self, board: chess.Board, wdl_only: bool = False) -> int: if board.castling_rights: raise KeyError(f"gaviota tables do not contain positions with castling rights: {board.fen()}") - if chess.popcount(board.occupied) > 5: - raise KeyError(f"gaviota tables support up to 5 pieces, not {chess.popcount(board.occupied)}: {board.fen()}") + if board.piece_count() > 5: + raise KeyError(f"gaviota tables support up to 5 pieces, not {board.piece_count()}: {board.fen()}") stm = ctypes.c_uint(0 if board.turn == chess.WHITE else 1) ep_square = ctypes.c_uint(board.ep_square if board.ep_square else 64) diff --git a/chess/syzygy.py b/chess/syzygy.py index 0c6b7822c..2250db5b5 100644 --- a/chess/syzygy.py +++ b/chess/syzygy.py @@ -1548,8 +1548,8 @@ def probe_wdl_table(self, board: chess.Board) -> int: try: table = typing.cast(WdlTable, self.wdl[key]) except KeyError: - if chess.popcount(board.occupied) > TBPIECES: - raise KeyError(f"syzygy tables support up to {TBPIECES} pieces, not {chess.popcount(board.occupied)}: {board.fen()}") + if board.piece_count() > TBPIECES: + raise KeyError(f"syzygy tables support up to {TBPIECES} pieces, not {board.piece_count()}: {board.fen()}") raise MissingTableError(f"did not find wdl table {key}") self._bump_lru(table) @@ -1567,8 +1567,8 @@ def probe_ab(self, board: chess.Board, alpha: int, beta: int, threats: bool = Fa # positions that have more pieces than the maximum number of supported # pieces. We artificially limit this to one additional level, to # make sure search remains somewhat bounded. - if chess.popcount(board.occupied) > TBPIECES + 1: - raise KeyError(f"syzygy tables support up to {TBPIECES} pieces, not {chess.popcount(board.occupied)}: {board.fen()}") + if board.piece_count() > TBPIECES + 1: + raise KeyError(f"syzygy tables support up to {TBPIECES} pieces, not {board.piece_count()}: {board.fen()}") # Special case: Variant with compulsory captures. if self.variant.captures_compulsory: @@ -1613,7 +1613,7 @@ def sprobe_ab(self, board: chess.Board, alpha: int, beta: int, threats: bool = F threats_found = False - if threats or chess.popcount(board.occupied) >= 6: + if threats or board.piece_count() >= 6: for threat in board.generate_legal_moves(~board.pawns): board.push(threat) try: diff --git a/test.py b/test.py index 4927a2b80..228c62f3f 100755 --- a/test.py +++ b/test.py @@ -1220,7 +1220,7 @@ def test_clear(self): self.assertFalse(board.ep_square) self.assertFalse(board.piece_at(chess.E1)) - self.assertEqual(chess.popcount(board.occupied), 0) + self.assertEqual(board.piece_count(), 0) def test_threefold_repetition(self): board = chess.Board() From a345dbd131fb5cbcbffdc9e50901d359480926c5 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 3 Apr 2026 21:24:06 +0200 Subject: [PATCH 116/116] Document board.piece_count() --- CHANGELOG.rst | 9 --------- chess/__init__.py | 7 ++++++- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 212db89b1..03a9555d1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,15 +1,6 @@ Changelog for python-chess ========================== -New in unreleased (27th Mar 2026) ---------------------------------- - -Bugfixes: -* Fixed typo in README.rst. - -Changes: -* Added ``board.piece_count`` function. - New in v1.11.2 (25th Feb 2025) ------------------------------ diff --git a/chess/__init__.py b/chess/__init__.py index 12b249bd5..9ea44f36e 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -841,8 +841,13 @@ def clear_board(self) -> None: :class:`~chess.Board` also clears the move stack. """ self._clear_board() - + def piece_count(self) -> int: + """ + Gets the number of pieces on the board. + + Does not include Crazyhouse pockets. + """ return popcount(self.occupied) def pieces_mask(self, piece_type: PieceType, color: Color) -> Bitboard: