Skip to content

Commit 5d8e82d

Browse files
committed
Let variant boards manage their own stack
1 parent dda9e8f commit 5d8e82d

File tree

2 files changed

+53
-22
lines changed

2 files changed

+53
-22
lines changed

chess/__init__.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,7 +1542,7 @@ def from_chess960_pos(cls: Type[BaseBoardT], scharnagl: int) -> BaseBoardT:
15421542

15431543
BoardT = TypeVar("BoardT", bound="Board")
15441544

1545-
class _BoardState(Generic[BoardT]):
1545+
class _BoardState:
15461546

15471547
def __init__(self, board: BoardT) -> None:
15481548
self.pawns = board.pawns
@@ -1701,7 +1701,7 @@ def __init__(self: BoardT, fen: Optional[str] = STARTING_FEN, *, chess960: bool
17011701

17021702
self.ep_square = None
17031703
self.move_stack = []
1704-
self._stack: List[_BoardState[BoardT]] = []
1704+
self._stack: List[_BoardState] = []
17051705

17061706
if fen is None:
17071707
self.clear()
@@ -2304,9 +2304,6 @@ def is_repetition(self, count: int = 3) -> bool:
23042304

23052305
return False
23062306

2307-
def _board_state(self: BoardT) -> _BoardState[BoardT]:
2308-
return _BoardState(self)
2309-
23102307
def _push_capture(self, move: Move, capture_square: Square, piece_type: PieceType, was_promoted: bool) -> None:
23112308
pass
23122309

@@ -2335,7 +2332,7 @@ def push(self: BoardT, move: Move) -> None:
23352332
"""
23362333
# Push move and remember board state.
23372334
move = self._to_chess960(move)
2338-
board_state = self._board_state()
2335+
board_state = _BoardState(self)
23392336
self.castling_rights = self.clean_castling_rights() # Before pushing stack
23402337
self.move_stack.append(self._from_chess960(self.chess960, move.from_square, move.to_square, move.promotion, move.drop))
23412338
self._stack.append(board_state)

chess/variant.py

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -673,14 +673,12 @@ def status(self) -> chess.Status:
673673

674674
ThreeCheckBoardT = TypeVar("ThreeCheckBoardT", bound="ThreeCheckBoard")
675675

676-
class _ThreeCheckBoardState(Generic[ThreeCheckBoardT], chess._BoardState[ThreeCheckBoardT]):
677-
def __init__(self, board: ThreeCheckBoardT) -> None:
678-
super().__init__(board)
676+
class _ThreeCheckBoardState:
677+
def __init__(self, board: ThreeCheckBoard) -> None:
679678
self.remaining_checks_w = board.remaining_checks[chess.WHITE]
680679
self.remaining_checks_b = board.remaining_checks[chess.BLACK]
681680

682-
def restore(self, board: ThreeCheckBoardT) -> None:
683-
super().restore(board)
681+
def restore(self, board: ThreeCheckBoard) -> None:
684682
board.remaining_checks[chess.WHITE] = self.remaining_checks_w
685683
board.remaining_checks[chess.BLACK] = self.remaining_checks_b
686684

@@ -698,8 +696,13 @@ class ThreeCheckBoard(chess.Board):
698696

699697
def __init__(self, fen: Optional[str] = starting_fen, chess960: bool = False) -> None:
700698
self.remaining_checks = [3, 3]
699+
self._three_check_stack: List[_ThreeCheckBoardState] = []
701700
super().__init__(fen, chess960=chess960)
702701

702+
def clear_stack(self) -> None:
703+
super().clear_stack()
704+
self._three_check_stack.clear()
705+
703706
def reset_board(self) -> None:
704707
super().reset_board()
705708
self.remaining_checks[chess.WHITE] = 3
@@ -710,14 +713,17 @@ def clear_board(self) -> None:
710713
self.remaining_checks[chess.WHITE] = 3
711714
self.remaining_checks[chess.BLACK] = 3
712715

713-
def _board_state(self: ThreeCheckBoardT) -> _ThreeCheckBoardState[ThreeCheckBoardT]:
714-
return _ThreeCheckBoardState(self)
715-
716716
def push(self, move: chess.Move) -> None:
717+
self._three_check_stack.append(_ThreeCheckBoardState(self))
717718
super().push(move)
718719
if self.is_check():
719720
self.remaining_checks[not self.turn] -= 1
720721

722+
def pop(self) -> chess.Move:
723+
move = super().pop()
724+
self._three_check_stack.pop().restore(self)
725+
return move
726+
721727
def has_insufficient_material(self, color: chess.Color) -> bool:
722728
# Any remaining piece can give check.
723729
return not (self.occupied_co[color] & ~self.kings)
@@ -792,8 +798,19 @@ def _transposition_key(self) -> Hashable:
792798
def copy(self: ThreeCheckBoardT, stack: Union[bool, int] = True) -> ThreeCheckBoardT:
793799
board = super().copy(stack=stack)
794800
board.remaining_checks = self.remaining_checks.copy()
801+
if stack:
802+
stack = len(self.move_stack) if stack is True else stack
803+
board._three_check_stack = self._three_check_stack[-stack:]
795804
return board
796805

806+
def root(self: ThreeCheckBoardT) -> ThreeCheckBoardT:
807+
if self._three_check_stack:
808+
board = super().root()
809+
self._three_check_stack[0].restore(board)
810+
return board
811+
else:
812+
return self.copy(stack=False)
813+
797814
def mirror(self: ThreeCheckBoardT) -> ThreeCheckBoardT:
798815
board = super().mirror()
799816
board.remaining_checks[chess.WHITE] = self.remaining_checks[chess.BLACK]
@@ -803,14 +820,12 @@ def mirror(self: ThreeCheckBoardT) -> ThreeCheckBoardT:
803820

804821
CrazyhouseBoardT = TypeVar("CrazyhouseBoardT", bound="CrazyhouseBoard")
805822

806-
class _CrazyhouseBoardState(Generic[CrazyhouseBoardT], chess._BoardState[CrazyhouseBoardT]):
807-
def __init__(self, board: CrazyhouseBoardT) -> None:
808-
super().__init__(board)
823+
class _CrazyhouseBoardState:
824+
def __init__(self, board: CrazyhouseBoard) -> None:
809825
self.pockets_w = board.pockets[chess.WHITE].copy()
810826
self.pockets_b = board.pockets[chess.BLACK].copy()
811827

812-
def restore(self, board: CrazyhouseBoardT) -> None:
813-
super().restore(board)
828+
def restore(self, board: CrazyhouseBoard) -> None:
814829
board.pockets[chess.WHITE] = self.pockets_w
815830
board.pockets[chess.BLACK] = self.pockets_b
816831

@@ -870,8 +885,13 @@ class CrazyhouseBoard(chess.Board):
870885

871886
def __init__(self, fen: Optional[str] = starting_fen, chess960: bool = False) -> None:
872887
self.pockets = [CrazyhousePocket(), CrazyhousePocket()]
888+
self._crazyhouse_stack: List[_CrazyhouseBoardState] = []
873889
super().__init__(fen, chess960=chess960)
874890

891+
def clear_stack(self) -> None:
892+
super().clear_stack()
893+
self._crazyhouse_stack.clear()
894+
875895
def reset_board(self) -> None:
876896
super().reset_board()
877897
self.pockets[chess.WHITE].reset()
@@ -882,10 +902,8 @@ def clear_board(self) -> None:
882902
self.pockets[chess.WHITE].reset()
883903
self.pockets[chess.BLACK].reset()
884904

885-
def _board_state(self: CrazyhouseBoardT) -> _CrazyhouseBoardState[CrazyhouseBoardT]:
886-
return _CrazyhouseBoardState(self)
887-
888905
def push(self, move: chess.Move) -> None:
906+
self._crazyhouse_stack.append(_CrazyhouseBoardState(self))
889907
super().push(move)
890908
if move.drop:
891909
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
896914
else:
897915
self.pockets[self.turn].add(piece_type)
898916

917+
def pop(self) -> chess.Move:
918+
move = super().pop()
919+
self._crazyhouse_stack.pop().restore(self)
920+
return move
921+
899922
def _is_halfmoves(self, n: int) -> bool:
900923
# No draw by 50-move rule or 75-move rule.
901924
return False
@@ -1028,8 +1051,19 @@ def copy(self: CrazyhouseBoardT, stack: Union[bool, int] = True) -> CrazyhouseBo
10281051
board = super().copy(stack=stack)
10291052
board.pockets[chess.WHITE] = self.pockets[chess.WHITE].copy()
10301053
board.pockets[chess.BLACK] = self.pockets[chess.BLACK].copy()
1054+
if stack:
1055+
stack = len(self.move_stack) if stack is True else stack
1056+
board._crazyhouse_stack = self._crazyhouse_stack[-stack:]
10311057
return board
10321058

1059+
def root(self: CrazyhouseBoardT) -> CrazyhouseBoardT:
1060+
if self._crazyhouse_stack:
1061+
board = super().root()
1062+
self._crazyhouse_stack[0].restore(board)
1063+
return board
1064+
else:
1065+
return self.copy(stack=False)
1066+
10331067
def mirror(self: CrazyhouseBoardT) -> CrazyhouseBoardT:
10341068
board = super().mirror()
10351069
board.pockets[chess.WHITE] = self.pockets[chess.BLACK].copy()

0 commit comments

Comments
 (0)