API Reference

A checkers board stored as black, white, and king 32-bit masks.

Source code in src/clatsop/ruleset.py
@dataclass(frozen=True, slots=True)
class Board:
    """A checkers board stored as black, white, and king 32-bit masks."""

    black: int
    white: int
    kings: int = 0

    def __post_init__(self) -> None:
        black, white, kings = validate_board_masks(self.black, self.white, self.kings)
        object.__setattr__(self, "black", black)
        object.__setattr__(self, "white", white)
        object.__setattr__(self, "kings", kings)

    @classmethod
    def initial(cls) -> Board:
        """Return the standard starting board."""

        return cls(INITIAL_BLACK, INITIAL_WHITE, 0)

    @classmethod
    def from_tuple(cls, values: object) -> Board:
        """Create a board from ``(black, white, kings)``."""

        values = tuple(values)  # type: ignore[arg-type]
        if len(values) != 3:
            raise ValueError("board tuple must have three fields")
        return cls(*validate_board_masks(*values))

    @property
    def occupied(self) -> int:
        """Return the occupied-square mask."""

        return self.black | self.white

    @property
    def empty(self) -> int:
        """Return the empty playable-square mask."""

        return (~self.occupied) & MASK32

    @property
    def key(self) -> tuple[int, int, int]:
        """Return a hashable native tuple key."""

        return self.as_tuple()

    @property
    def maps(self) -> dict[str, int]:
        """Return a plain dict of the board masks."""

        return self.as_dict()

    def as_tuple(self) -> tuple[int, int, int]:
        """Return ``(black, white, kings)``."""

        return self.black, self.white, self.kings

    def as_dict(self) -> dict[str, int]:
        """Return a plain dict of 32-bit masks."""

        return {"black": self.black, "white": self.white, "kings": self.kings}

    def display(
        self,
        size: float = 3,
        title: str | None = None,
        show: bool = True,
    ) -> tuple[Any, Any]:
        """Render this board with matplotlib and return ``(figure, axis)``."""

        from .display import show_board

        return show_board(self, size=size, title=title, show=show)

occupied property

occupied: int

Return the occupied-square mask.

empty property

empty: int

Return the empty playable-square mask.

key property

key: tuple[int, int, int]

Return a hashable native tuple key.

maps property

maps: dict[str, int]

Return a plain dict of the board masks.

initial classmethod

initial() -> Board

Return the standard starting board.

Source code in src/clatsop/ruleset.py
@classmethod
def initial(cls) -> Board:
    """Return the standard starting board."""

    return cls(INITIAL_BLACK, INITIAL_WHITE, 0)

from_tuple classmethod

from_tuple(values: object) -> Board

Create a board from (black, white, kings).

Source code in src/clatsop/ruleset.py
@classmethod
def from_tuple(cls, values: object) -> Board:
    """Create a board from ``(black, white, kings)``."""

    values = tuple(values)  # type: ignore[arg-type]
    if len(values) != 3:
        raise ValueError("board tuple must have three fields")
    return cls(*validate_board_masks(*values))

as_tuple

as_tuple() -> tuple[int, int, int]

Return (black, white, kings).

Source code in src/clatsop/ruleset.py
def as_tuple(self) -> tuple[int, int, int]:
    """Return ``(black, white, kings)``."""

    return self.black, self.white, self.kings

as_dict

as_dict() -> dict[str, int]

Return a plain dict of 32-bit masks.

Source code in src/clatsop/ruleset.py
def as_dict(self) -> dict[str, int]:
    """Return a plain dict of 32-bit masks."""

    return {"black": self.black, "white": self.white, "kings": self.kings}

display

display(size: float = 3, title: str | None = None, show: bool = True) -> tuple[Any, Any]

Render this board with matplotlib and return (figure, axis).

Source code in src/clatsop/ruleset.py
def display(
    self,
    size: float = 3,
    title: str | None = None,
    show: bool = True,
) -> tuple[Any, Any]:
    """Render this board with matplotlib and return ``(figure, axis)``."""

    from .display import show_board

    return show_board(self, size=size, title=title, show=show)

A board plus side to move and optional non-identity metadata.

Source code in src/clatsop/ruleset.py
@dataclass(frozen=True, slots=True)
class Turn:
    """A board plus side to move and optional non-identity metadata."""

    board: Board
    black_to_move: int = 1
    metadata: tuple[tuple[object, object], ...] = field(default=(), compare=False, hash=False)

    def __post_init__(self) -> None:
        board = self.board if isinstance(self.board, Board) else Board.from_tuple(self.board)
        black_to_move = _turn_bit(self.black_to_move)
        metadata = _metadata_tuple(self.metadata)
        object.__setattr__(self, "board", board)
        object.__setattr__(self, "black_to_move", black_to_move)
        object.__setattr__(self, "metadata", metadata)

    @classmethod
    def initial(cls) -> Turn:
        """Return the standard starting turn."""

        return cls(Board.initial(), 1)

    @classmethod
    def from_masks(
        cls,
        black: object,
        white: object,
        kings: object = 0,
        black_to_move: object = 1,
        metadata: object = (),
    ) -> Turn:
        """Create a turn from board masks and side to move."""

        board = Board(*validate_board_masks(black, white, kings))
        return cls(board, _turn_bit(black_to_move), _metadata_tuple(metadata))

    @classmethod
    def from_tuple(cls, values: object) -> Turn:
        """Create a turn from the four-field state key."""

        values = tuple(values)  # type: ignore[arg-type]
        if len(values) == 4:
            return cls.from_masks(*values)
        raise ValueError("turn tuple must have four fields")

    @classmethod
    def from_packed(cls, state: object, metadata: object = ()) -> Turn:
        """Create a validated turn from one packed 97-bit state integer."""

        return cls.from_masks(*unpack_state_values(state), metadata=metadata)

    @property
    def black(self) -> int:
        return self.board.black

    @property
    def white(self) -> int:
        return self.board.white

    @property
    def kings(self) -> int:
        return self.board.kings

    @property
    def occupied(self) -> int:
        return self.board.occupied

    @property
    def empty(self) -> int:
        return self.board.empty

    @property
    def side(self) -> str:
        """Return ``"black"`` or ``"white"`` for the side to move."""

        return "black" if self.black_to_move else "white"

    @property
    def key(self) -> StateKey:
        """Return the hashable game-state key, excluding analysis metadata."""

        return self.as_tuple()

    @property
    def packed(self) -> PackedState:
        """Return one compact 97-bit integer suitable for hash tables."""

        return pack_validated_state(*self.as_tuple())

    @property
    def maps(self) -> dict[str, object]:
        """Return a plain dictionary of board and turn masks."""

        return self.as_dict()

    def as_tuple(self) -> StateKey:
        """Return ``(black, white, kings, black_to_move)``."""

        return *self.board.as_tuple(), self.black_to_move

    def as_dict(self) -> dict[str, object]:
        """Return a plain dict for dataframe or ad hoc analysis use."""

        record: dict[str, object] = {**self.board.as_dict(), "black_to_move": self.black_to_move}
        if self.metadata:
            record["metadata"] = dict(self.metadata)
        return record

    def display(
        self,
        size: float = 3,
        title: str | None = None,
        show: bool = True,
    ) -> tuple[Any, Any]:
        """Render this turn with matplotlib and return ``(figure, axis)``."""

        from .display import show_turn

        return show_turn(self, size=size, title=title, show=show)

side property

side: str

Return "black" or "white" for the side to move.

key property

key: StateKey

Return the hashable game-state key, excluding analysis metadata.

packed property

packed: PackedState

Return one compact 97-bit integer suitable for hash tables.

maps property

maps: dict[str, object]

Return a plain dictionary of board and turn masks.

initial classmethod

initial() -> Turn

Return the standard starting turn.

Source code in src/clatsop/ruleset.py
@classmethod
def initial(cls) -> Turn:
    """Return the standard starting turn."""

    return cls(Board.initial(), 1)

from_masks classmethod

from_masks(black: object, white: object, kings: object = 0, black_to_move: object = 1, metadata: object = ()) -> Turn

Create a turn from board masks and side to move.

Source code in src/clatsop/ruleset.py
@classmethod
def from_masks(
    cls,
    black: object,
    white: object,
    kings: object = 0,
    black_to_move: object = 1,
    metadata: object = (),
) -> Turn:
    """Create a turn from board masks and side to move."""

    board = Board(*validate_board_masks(black, white, kings))
    return cls(board, _turn_bit(black_to_move), _metadata_tuple(metadata))

from_tuple classmethod

from_tuple(values: object) -> Turn

Create a turn from the four-field state key.

Source code in src/clatsop/ruleset.py
@classmethod
def from_tuple(cls, values: object) -> Turn:
    """Create a turn from the four-field state key."""

    values = tuple(values)  # type: ignore[arg-type]
    if len(values) == 4:
        return cls.from_masks(*values)
    raise ValueError("turn tuple must have four fields")

from_packed classmethod

from_packed(state: object, metadata: object = ()) -> Turn

Create a validated turn from one packed 97-bit state integer.

Source code in src/clatsop/ruleset.py
@classmethod
def from_packed(cls, state: object, metadata: object = ()) -> Turn:
    """Create a validated turn from one packed 97-bit state integer."""

    return cls.from_masks(*unpack_state_values(state), metadata=metadata)

as_tuple

as_tuple() -> StateKey

Return (black, white, kings, black_to_move).

Source code in src/clatsop/ruleset.py
def as_tuple(self) -> StateKey:
    """Return ``(black, white, kings, black_to_move)``."""

    return *self.board.as_tuple(), self.black_to_move

as_dict

as_dict() -> dict[str, object]

Return a plain dict for dataframe or ad hoc analysis use.

Source code in src/clatsop/ruleset.py
def as_dict(self) -> dict[str, object]:
    """Return a plain dict for dataframe or ad hoc analysis use."""

    record: dict[str, object] = {**self.board.as_dict(), "black_to_move": self.black_to_move}
    if self.metadata:
        record["metadata"] = dict(self.metadata)
    return record

display

display(size: float = 3, title: str | None = None, show: bool = True) -> tuple[Any, Any]

Render this turn with matplotlib and return (figure, axis).

Source code in src/clatsop/ruleset.py
def display(
    self,
    size: float = 3,
    title: str | None = None,
    show: bool = True,
) -> tuple[Any, Any]:
    """Render this turn with matplotlib and return ``(figure, axis)``."""

    from .display import show_turn

    return show_turn(self, size=size, title=title, show=show)

A primitive rule stored as three 32-bit masks and four packed flags.

Source code in src/clatsop/ruleset.py
@dataclass(frozen=True, slots=True)
class Rule:
    """A primitive rule stored as three 32-bit masks and four packed flags."""

    is_black: int
    is_white: int
    is_empty: int
    flags: int

    def __post_init__(self) -> None:
        is_black = _mask32(self.is_black, "is_black")
        is_white = _mask32(self.is_white, "is_white")
        is_empty = _mask32(self.is_empty, "is_empty")
        flags = _integer(self.flags, "rule flags")
        if flags < 0 or flags & ~RULE_FLAG_MASK:
            raise ValueError("rule flags must fit in the four defined bits")
        if is_black & is_white or is_black & is_empty or is_white & is_empty:
            raise ValueError("rule masks must be disjoint")

        object.__setattr__(self, "is_black", is_black)
        object.__setattr__(self, "is_white", is_white)
        object.__setattr__(self, "is_empty", is_empty)
        object.__setattr__(self, "flags", flags)

        if self.mover.bit_count() != 1:
            raise ValueError("a rule must require exactly one moving piece")
        if self.is_empty.bit_count() != 1:
            raise ValueError("a rule must require exactly one destination square")
        opponent = self.is_white if self.black_to_move else self.is_black
        if opponent.bit_count() != self.capture:
            raise ValueError("the capture flag must match one required opponent")
        if self.promotion != _promotion_for_mask(self.black_to_move, self.king, self.is_empty):
            raise ValueError("the promotion flag does not match the destination")

    @classmethod
    def from_masks(
        cls,
        is_black: object,
        is_white: object,
        is_empty: object,
        black_to_move: object,
        king: object = 0,
        promotion: object | None = None,
        capture: object | None = None,
    ) -> Rule:
        """Create a rule from masks and unpacked metadata bits."""

        black_to_move = _turn_bit(black_to_move)
        king = _flag_bit(king, "king")
        is_black = _mask32(is_black, "is_black")
        is_white = _mask32(is_white, "is_white")
        is_empty = _mask32(is_empty, "is_empty")
        opponent = is_white if black_to_move else is_black
        capture = bool(opponent) if capture is None else _flag_bit(capture, "capture")
        if promotion is None:
            promotion = _promotion_for_mask(black_to_move, king, is_empty)
        else:
            promotion = _flag_bit(promotion, "promotion")
        return cls(
            is_black,
            is_white,
            is_empty,
            _pack_rule_flags(black_to_move, king, promotion, capture),
        )

    @classmethod
    def from_record(cls, record: Mapping[str, object]) -> Rule:
        """Create a rule from a compact flat record."""

        if "flags" in record:
            return cls(
                _mask32(record["is_black"], "is_black"),
                _mask32(record["is_white"], "is_white"),
                _mask32(record["is_empty"], "is_empty"),
                _integer(record["flags"], "rule flags"),
            )
        return cls.from_masks(
            record["is_black"],
            record["is_white"],
            record["is_empty"],
            record["black_to_move"],
            record.get("king", 0),
            record.get("promotion"),
            record.get("capture"),
        )

    def as_tuple(self) -> RuleKey:
        """Return the four stored values as a hashable tuple."""

        return self.is_black, self.is_white, self.is_empty, self.flags

    def as_dict(self) -> dict[str, int]:
        """Return an analysis-friendly record with unpacked metadata bits."""

        return {
            "is_black": self.is_black,
            "is_white": self.is_white,
            "is_empty": self.is_empty,
            "black_to_move": self.black_to_move,
            "king": self.king,
            "promotion": self.promotion,
            "capture": self.capture,
        }

    @property
    def black_to_move(self) -> int:
        """Return the packed side-to-move bit."""

        return int(bool(self.flags & FLAG_BLACK_TO_MOVE))

    @property
    def king(self) -> int:
        """Return the packed king-required bit."""

        return int(bool(self.flags & FLAG_KING))

    @property
    def promotion(self) -> int:
        """Return the packed promotion bit."""

        return int(bool(self.flags & FLAG_PROMOTION))

    @property
    def capture(self) -> int:
        """Return the packed capture bit."""

        return int(bool(self.flags & FLAG_CAPTURE))

    @property
    def mover(self) -> int:
        """Return the required mover-square mask."""

        return self.is_black if self.black_to_move else self.is_white

    def applies(self, turn: object) -> bool:
        """Return whether this rule's conditions match a turn."""

        turn = as_turn(turn)
        return _rule_applies_values(
            *turn.as_tuple(),
            self.is_black,
            self.is_white,
            self.is_empty,
            self.flags,
        )

    def apply(self, turn: object, switch_side: bool = True) -> Turn:
        """Apply this rule to a matching turn."""

        turn = as_turn(turn)
        if not self.applies(turn):
            raise ValueError("rule conditions are not satisfied by the turn")

        next_black, next_white, next_kings, next_side = _apply_rule_values(
            *turn.as_tuple(),
            self.is_black,
            self.is_white,
            self.is_empty,
            self.flags,
            switch_side,
        )
        return Turn(Board(next_black, next_white, next_kings), next_side, turn.metadata)

    def display(
        self,
        size: float = 3.2,
        title: str | None = None,
        show: bool = True,
    ) -> tuple[Any, Any]:
        """Render this primitive rule and return ``(figure, axes)``."""

        from .display import show_ruleset_rows

        return show_ruleset_rows([self], size=size, title=title, show=show)[0]

black_to_move property

black_to_move: int

Return the packed side-to-move bit.

king property

king: int

Return the packed king-required bit.

promotion property

promotion: int

Return the packed promotion bit.

capture property

capture: int

Return the packed capture bit.

mover property

mover: int

Return the required mover-square mask.

from_masks classmethod

from_masks(is_black: object, is_white: object, is_empty: object, black_to_move: object, king: object = 0, promotion: object | None = None, capture: object | None = None) -> Rule

Create a rule from masks and unpacked metadata bits.

Source code in src/clatsop/ruleset.py
@classmethod
def from_masks(
    cls,
    is_black: object,
    is_white: object,
    is_empty: object,
    black_to_move: object,
    king: object = 0,
    promotion: object | None = None,
    capture: object | None = None,
) -> Rule:
    """Create a rule from masks and unpacked metadata bits."""

    black_to_move = _turn_bit(black_to_move)
    king = _flag_bit(king, "king")
    is_black = _mask32(is_black, "is_black")
    is_white = _mask32(is_white, "is_white")
    is_empty = _mask32(is_empty, "is_empty")
    opponent = is_white if black_to_move else is_black
    capture = bool(opponent) if capture is None else _flag_bit(capture, "capture")
    if promotion is None:
        promotion = _promotion_for_mask(black_to_move, king, is_empty)
    else:
        promotion = _flag_bit(promotion, "promotion")
    return cls(
        is_black,
        is_white,
        is_empty,
        _pack_rule_flags(black_to_move, king, promotion, capture),
    )

from_record classmethod

from_record(record: Mapping[str, object]) -> Rule

Create a rule from a compact flat record.

Source code in src/clatsop/ruleset.py
@classmethod
def from_record(cls, record: Mapping[str, object]) -> Rule:
    """Create a rule from a compact flat record."""

    if "flags" in record:
        return cls(
            _mask32(record["is_black"], "is_black"),
            _mask32(record["is_white"], "is_white"),
            _mask32(record["is_empty"], "is_empty"),
            _integer(record["flags"], "rule flags"),
        )
    return cls.from_masks(
        record["is_black"],
        record["is_white"],
        record["is_empty"],
        record["black_to_move"],
        record.get("king", 0),
        record.get("promotion"),
        record.get("capture"),
    )

as_tuple

as_tuple() -> RuleKey

Return the four stored values as a hashable tuple.

Source code in src/clatsop/ruleset.py
def as_tuple(self) -> RuleKey:
    """Return the four stored values as a hashable tuple."""

    return self.is_black, self.is_white, self.is_empty, self.flags

as_dict

as_dict() -> dict[str, int]

Return an analysis-friendly record with unpacked metadata bits.

Source code in src/clatsop/ruleset.py
def as_dict(self) -> dict[str, int]:
    """Return an analysis-friendly record with unpacked metadata bits."""

    return {
        "is_black": self.is_black,
        "is_white": self.is_white,
        "is_empty": self.is_empty,
        "black_to_move": self.black_to_move,
        "king": self.king,
        "promotion": self.promotion,
        "capture": self.capture,
    }

applies

applies(turn: object) -> bool

Return whether this rule's conditions match a turn.

Source code in src/clatsop/ruleset.py
def applies(self, turn: object) -> bool:
    """Return whether this rule's conditions match a turn."""

    turn = as_turn(turn)
    return _rule_applies_values(
        *turn.as_tuple(),
        self.is_black,
        self.is_white,
        self.is_empty,
        self.flags,
    )

apply

apply(turn: object, switch_side: bool = True) -> Turn

Apply this rule to a matching turn.

Source code in src/clatsop/ruleset.py
def apply(self, turn: object, switch_side: bool = True) -> Turn:
    """Apply this rule to a matching turn."""

    turn = as_turn(turn)
    if not self.applies(turn):
        raise ValueError("rule conditions are not satisfied by the turn")

    next_black, next_white, next_kings, next_side = _apply_rule_values(
        *turn.as_tuple(),
        self.is_black,
        self.is_white,
        self.is_empty,
        self.flags,
        switch_side,
    )
    return Turn(Board(next_black, next_white, next_kings), next_side, turn.metadata)

display

display(size: float = 3.2, title: str | None = None, show: bool = True) -> tuple[Any, Any]

Render this primitive rule and return (figure, axes).

Source code in src/clatsop/ruleset.py
def display(
    self,
    size: float = 3.2,
    title: str | None = None,
    show: bool = True,
) -> tuple[Any, Any]:
    """Render this primitive rule and return ``(figure, axes)``."""

    from .display import show_ruleset_rows

    return show_ruleset_rows([self], size=size, title=title, show=show)[0]

A completed legal turn represented by primitive rule indexes and its result.

Source code in src/clatsop/ruleset.py
@dataclass(frozen=True, slots=True)
class Transition:
    """A completed legal turn represented by primitive rule indexes and its result."""

    rule_indices: RulePath
    turn: Turn

    def __post_init__(self) -> None:
        rule_indices = tuple(_integer(index, "rule index") for index in self.rule_indices)
        if not rule_indices:
            raise ValueError("a transition must contain at least one primitive rule")
        object.__setattr__(self, "rule_indices", rule_indices)
        object.__setattr__(self, "turn", as_turn(self.turn))

    @property
    def key(self) -> tuple[RulePath, StateKey]:
        """Return a hashable path/result key."""

        return self.rule_indices, self.turn.key

    def as_dict(self) -> dict[str, object]:
        """Return a plain transition record."""

        return {"rule_indices": self.rule_indices, "turn": self.turn.as_dict()}

key property

key: tuple[RulePath, StateKey]

Return a hashable path/result key.

as_dict

as_dict() -> dict[str, object]

Return a plain transition record.

Source code in src/clatsop/ruleset.py
def as_dict(self) -> dict[str, object]:
    """Return a plain transition record."""

    return {"rule_indices": self.rule_indices, "turn": self.turn.as_dict()}

Compact primitive rules with bitwise runtime indexes and Python views.

Source code in src/clatsop/ruleset.py
class Ruleset:
    """Compact primitive rules with bitwise runtime indexes and Python views."""

    __slots__ = (
        "_is_black",
        "_is_white",
        "_is_empty",
        "_flags",
        "_candidate_rows",
        "_rule_indices",
        "_rules_by_side",
        "_rules_by_metadata",
    )

    def __init__(self, rules: Iterable[object]):
        rule_tuple = tuple(_coerce_rule(rule) for rule in rules)
        rule_indices: dict[RuleKey, int] = {}
        for index, rule in enumerate(rule_tuple):
            key = rule.as_tuple()
            if key in rule_indices:
                raise ValueError(f"duplicate rule at indexes {rule_indices[key]} and {index}")
            rule_indices[key] = index
        self._is_black = array("I", (rule.is_black for rule in rule_tuple))
        self._is_white = array("I", (rule.is_white for rule in rule_tuple))
        self._is_empty = array("I", (rule.is_empty for rule in rule_tuple))
        self._flags = array("B", (rule.flags for rule in rule_tuple))
        runtime_rows = tuple(
            (
                index,
                rule.is_black,
                rule.is_white,
                rule.is_empty,
                rule.flags,
                rule.mover,
            )
            for index, rule in enumerate(rule_tuple)
        )
        self._candidate_rows = _runtime_candidate_rows(runtime_rows)
        self._rule_indices = rule_indices
        self._rules_by_side = _rules_by_side(rule_tuple)
        self._rules_by_metadata = _rules_by_metadata(rule_tuple)

    @property
    def rules(self) -> tuple[Rule, ...]:
        """Return the immutable primitive-rule tuple."""

        return tuple(self)

    @property
    def records(self) -> tuple[RuleRecord, ...]:
        """Return independent plain dictionaries for all primitive rules."""

        return tuple(self.record(index) for index in range(len(self)))

    @property
    def record_map(self) -> dict[int, RuleRecord]:
        """Return an independent ``{rule_index: record}`` dictionary."""

        return {index: self.record(index) for index in range(len(self))}

    @property
    def rule_set(self) -> set[RuleKey]:
        """Return an independent set of hashable primitive-rule tuples."""

        return {
            (
                self._is_black[index],
                self._is_white[index],
                self._is_empty[index],
                self._flags[index],
            )
            for index in range(len(self))
        }

    @property
    def buffers(self) -> dict[str, memoryview]:
        """Return immutable typed snapshots of the packed runtime columns."""

        return {
            "is_black": memoryview(self._is_black.tobytes()).cast("I"),
            "is_white": memoryview(self._is_white.tobytes()).cast("I"),
            "is_empty": memoryview(self._is_empty.tobytes()).cast("I"),
            "flags": memoryview(self._flags.tobytes()).cast("B"),
        }

    @property
    def rules_by_side(self) -> dict[int, tuple[int, ...]]:
        """Return an independent side-to-rule-index mapping."""

        return dict(self._rules_by_side)

    @property
    def rules_by_metadata(self) -> dict[RuleMetadataKey, tuple[int, ...]]:
        """Return an independent metadata-to-rule-index mapping."""

        return dict(self._rules_by_metadata)

    @classmethod
    def american(cls, side: object = "both") -> Ruleset:
        """Return the standard American checkers primitive ruleset."""

        if side is None or str(side).lower() in ("both", "all"):
            return american_ruleset()
        return cls.from_board_geometry(side=side)

    @classmethod
    def from_board_geometry(cls, side: object = "both") -> Ruleset:
        """Build rules directly from the 32 playable-square board geometry."""

        rules = []
        for black_to_move in _runtime_sides(side):
            for template in _compact_move_templates():
                if _template_requires_king(black_to_move, template):
                    rules.append(_rule_from_template(template, black_to_move, king=1))
                else:
                    rules.append(_rule_from_template(template, black_to_move, king=0))
                    if _template_promotes(black_to_move, template):
                        rules.append(_rule_from_template(template, black_to_move, king=1))
        return cls(rules)

    @classmethod
    def from_records(cls, records: object) -> Ruleset:
        """Build a ruleset from flat records, rule objects, or a dataframe."""

        return cls(_records_from_any(records))

    def __len__(self) -> int:
        return len(self._flags)

    def __iter__(self) -> Iterator[Rule]:
        return (self[index] for index in range(len(self)))

    def __getitem__(self, rule_index: object) -> Rule:
        index = self._check_index(rule_index)
        return Rule(
            self._is_black[index],
            self._is_white[index],
            self._is_empty[index],
            self._flags[index],
        )

    def record(self, rule_index: object) -> RuleRecord:
        """Return one rule as a plain dictionary."""

        index = self._check_index(rule_index)
        return _indexed_record(index, self[index])

    def to_dataframe(self) -> Any:
        """Return three stored masks and four unpacked metadata bits."""

        import pandas as pd

        df = pd.DataFrame.from_records(self.records, columns=("rule_index", *RULE_COLUMNS))
        return df.set_index("rule_index").rename_axis(None)

    def select_records(
        self,
        rules: object | None = None,
        *,
        black_to_move: object | None = None,
        king: object | None = None,
        promotion: object | None = None,
        capture: object | None = None,
        sort_by: str | Iterable[str] | None = None,
        reverse: bool = False,
    ) -> list[RuleRecord]:
        """Return filtered and sorted rule records from common selection inputs."""

        black_to_move, king, promotion, capture = _rule_filter_values(
            black_to_move,
            king,
            promotion,
            capture,
        )
        records = None
        if rules is None:
            records = [dict(record) for record in self.records]
        elif isinstance(rules, int):
            records = [self.record(rules)]
        elif isinstance(rules, slice):
            records = [self.record(index) for index in range(len(self))[rules]]
        elif hasattr(rules, "to_dict") and hasattr(rules, "columns"):
            rows = []
            dataframe = cast(Any, rules)
            for index, record in dataframe.to_dict("index").items():
                record = dict(record)
                record.setdefault("rule_index", index)
                rows.append(record)
            records = rows
        else:
            values = list(cast(Iterable[object], rules))
            if all(isinstance(value, int) for value in values):
                records = [self.record(index) for index in values]
            else:
                records = []
                for value in values:
                    if isinstance(value, Rule):
                        key = value.as_tuple()
                        if key not in self._rule_indices:
                            raise ValueError("selected rule does not belong to this ruleset")
                        records.append(_indexed_record(self._rule_indices[key], value))
                    else:
                        records.append(dict(cast(Mapping[str, int], value)))

        return _query_rule_records(
            records,
            black_to_move=black_to_move,
            king=king,
            promotion=promotion,
            capture=capture,
            sort_by=sort_by,
            reverse=reverse,
        )

    def plot(
        self,
        rules: object | None = None,
        size: float = 3.2,
        title: str | None = None,
        show: bool = True,
        *,
        black_to_move: object | None = None,
        king: object | None = None,
        promotion: object | None = None,
        capture: object | None = None,
        sort_by: str | Iterable[str] | None = None,
        reverse: bool = False,
    ) -> list[tuple[Any, Any]]:
        """Plot selected rules with condition, effect, and summary boards."""

        from .display import show_ruleset_rows

        selected = self.select_records(
            rules,
            black_to_move=black_to_move,
            king=king,
            promotion=promotion,
            capture=capture,
            sort_by=sort_by,
            reverse=reverse,
        )
        return show_ruleset_rows(selected, size=size, title=title, show=show)

    def display(
        self,
        rules: object | None = None,
        size: float = 3.2,
        title: str | None = None,
        show: bool = True,
        *,
        black_to_move: object | None = None,
        king: object | None = None,
        promotion: object | None = None,
        capture: object | None = None,
        sort_by: str | Iterable[str] | None = None,
        reverse: bool = False,
    ) -> list[tuple[Any, Any]]:
        """Render selected rules with condition, effect, and summary boards."""

        return self.plot(
            rules=rules,
            size=size,
            title=title,
            show=show,
            black_to_move=black_to_move,
            king=king,
            promotion=promotion,
            capture=capture,
            sort_by=sort_by,
            reverse=reverse,
        )

    def indices_for(
        self,
        black_to_move: object | None = None,
        king: object | None = None,
        promotion: object | None = None,
        capture: object | None = None,
        *,
        sort_by: str | Iterable[str] | None = None,
        reverse: bool = False,
    ) -> list[int]:
        """Return rule indexes matching metadata filters."""

        black_to_move, king, promotion, capture = _rule_filter_values(
            black_to_move,
            king,
            promotion,
            capture,
        )
        if all(value is not None for value in (black_to_move, king, promotion, capture)):
            key = (
                black_to_move,
                king,
                promotion,
                capture,
            )
            indexes = list(self._rules_by_metadata.get(key, ()))
        else:
            indexes = []
            for key, key_indexes in self._rules_by_metadata.items():
                if all(
                    requested is None or value == requested
                    for value, requested in zip(key, (black_to_move, king, promotion, capture), strict=True)
                ):
                    indexes.extend(key_indexes)
            indexes.sort()

        if sort_by is None:
            return indexes
        records = _sort_rule_records([self.record(index) for index in indexes], sort_by, reverse)
        return [record["rule_index"] for record in records]

    def rules_for(
        self,
        *,
        black_to_move: object | None = None,
        king: object | None = None,
        promotion: object | None = None,
        capture: object | None = None,
        sort_by: str | Iterable[str] | None = None,
        reverse: bool = False,
    ) -> tuple[Rule, ...]:
        """Return rule objects matching metadata filters in the requested order."""

        indexes = self.indices_for(
            black_to_move,
            king,
            promotion,
            capture,
            sort_by=sort_by,
            reverse=reverse,
        )
        return tuple(self[index] for index in indexes)

    def matching_rule_indices(
        self,
        turn: object,
        *,
        capture: object | None = None,
        allow_promotions: object = True,
        from_mask: object | None = None,
    ) -> list[int]:
        """Return bitwise-matched rule indexes under runtime policy filters."""

        turn = as_turn(turn)
        capture = _optional_flag_bit(capture, "capture")
        capture_mode = None if capture is None else bool(capture)
        allow_promotions = bool(_flag_bit(allow_promotions, "allow_promotions"))
        if from_mask is not None:
            from_mask = _mask32(from_mask, "from_mask")
        return self._matching_indices(
            turn.black,
            turn.white,
            turn.kings,
            turn.black_to_move,
            capture_mode,
            allow_promotions,
            from_mask,
        )

    def matching_rules(
        self,
        turn: object,
        *,
        capture: object | None = None,
        allow_promotions: object = True,
        from_mask: object | None = None,
    ) -> tuple[Rule, ...]:
        """Return matching rule objects."""

        indexes = self.matching_rule_indices(
            turn,
            capture=capture,
            allow_promotions=allow_promotions,
            from_mask=from_mask,
        )
        return tuple(self[index] for index in indexes)

    def legal_rule_indices(self, turn: object, from_mask: object | None = None) -> list[int]:
        """Return legal primitive rule indexes, applying mandatory capture."""

        turn = as_turn(turn)
        captures = self._matching_indices(
            turn.black,
            turn.white,
            turn.kings,
            turn.black_to_move,
            True,
            True,
            None,
        )
        legal = captures or self._matching_indices(
            turn.black,
            turn.white,
            turn.kings,
            turn.black_to_move,
            False,
            True,
            None,
        )
        if from_mask is None:
            return legal
        from_mask = _mask32(from_mask, "from_mask")
        return [index for index in legal if self._mover(index) == from_mask]

    def legal_rules(self, turn: object, from_mask: object | None = None) -> tuple[Rule, ...]:
        """Return legal primitive rule objects."""

        return tuple(self[index] for index in self.legal_rule_indices(turn, from_mask=from_mask))

    def has_capture(self, turn: object, from_mask: object | None = None) -> bool:
        """Return whether the side to move has an immediately legal capture."""

        black, white, kings, black_to_move = _turn_values(turn)
        if from_mask is not None:
            from_mask = _mask32(from_mask, "from_mask")
        return self._has_matching(
            black,
            white,
            kings,
            black_to_move,
            True,
            True,
            from_mask,
        )

    def has_promotion(self, turn: object) -> bool:
        """Return whether any legal first-step move promotes a man."""

        return any(self._flags[index] & FLAG_PROMOTION for index in self.legal_rule_indices(turn))

    def apply_rule(
        self,
        turn: object,
        rule_index: object,
        switch_side: bool = True,
    ) -> Turn:
        """Apply one satisfied primitive rule to a turn."""

        turn = as_turn(turn)
        index = self._check_index(rule_index)
        values = turn.as_tuple()
        if not self._index_applies(*values, index):
            raise ValueError("rule conditions are not satisfied by the turn")
        black, white, kings, black_to_move = self._apply_index(*values, index, switch_side)
        return Turn.from_masks(black, white, kings, black_to_move, turn.metadata)

    def apply_key(
        self,
        turn: object,
        rule_index: object,
        switch_side: bool = True,
    ) -> StateKey:
        """Apply one rule and return a native four-integer state key."""

        values = _turn_values(turn)
        index = self._check_index(rule_index)
        if not self._index_applies(*values, index):
            raise ValueError("rule conditions are not satisfied by the turn")
        return self._apply_index(*values, index, switch_side)

    def apply_packed(
        self,
        state: object,
        rule_index: object,
        switch_side: bool = True,
    ) -> PackedState:
        """Apply one rule to a packed state and return a packed successor."""

        values = unpack_state_values(state)
        index = self._check_index(rule_index)
        if not self._index_applies(*values, index):
            raise ValueError("rule conditions are not satisfied by the turn")
        return pack_validated_state(*self._apply_index(*values, index, switch_side))

    def expand_keys(
        self,
        turn: object,
        *,
        allow_captures: bool = True,
        allow_promotions: bool = True,
    ) -> tuple[tuple[StateKey, ...], int]:
        """Return legal successor keys and packed capture/promotion/terminal status."""

        values = _turn_values(turn)
        successors, status = self._expand_values(values, allow_captures, allow_promotions)
        return tuple(successors), status

    def expand_packed(
        self,
        state: object,
        *,
        allow_captures: bool = True,
        allow_promotions: bool = True,
    ) -> tuple[tuple[PackedState, ...], int]:
        """Expand one packed state without allocating board or turn objects."""

        values = unpack_state_values(state)
        successors, status = self._expand_values(values, allow_captures, allow_promotions)
        return tuple(pack_validated_state(*successor) for successor in successors), status

    def expand_packed_unchecked(
        self,
        state: PackedState,
        *,
        allow_captures: bool = True,
        allow_promotions: bool = True,
    ) -> tuple[tuple[PackedState, ...], int]:
        """Expand a trusted packed state without repeating invariant checks."""

        values = unpack_validated_state(state)
        successors, status = self._expand_values(values, allow_captures, allow_promotions)
        return tuple(pack_validated_state(*successor) for successor in successors), status

    def expand_edges(
        self,
        turn: object,
        *,
        allow_captures: bool = True,
        allow_promotions: bool = True,
    ) -> tuple[tuple[StateEdge, ...], int]:
        """Return completed rule paths paired with native successor keys."""

        values = _turn_values(turn)
        edges, status = self._expand_value_edges(values, allow_captures, allow_promotions)
        return tuple(edges), status

    def expand_packed_edges(
        self,
        state: object,
        *,
        allow_captures: bool = True,
        allow_promotions: bool = True,
    ) -> tuple[tuple[PackedStateEdge, ...], int]:
        """Return completed rule paths paired with packed successor states."""

        values = unpack_state_values(state)
        edges, status = self._expand_value_edges(values, allow_captures, allow_promotions)
        return tuple((path, pack_validated_state(*successor)) for path, successor in edges), status

    def _expand_values(
        self,
        values: StateKey,
        allow_captures: bool,
        allow_promotions: bool,
    ) -> tuple[list[StateKey], int]:
        black, white, kings, black_to_move = values
        captures = self._matching_indices(
            black,
            white,
            kings,
            black_to_move,
            True,
            True,
            None,
        )
        if captures:
            status = CAPTURE_AVAILABLE
            if any(self._flags[index] & FLAG_PROMOTION for index in captures):
                status |= PROMOTION_AVAILABLE
            if not allow_captures:
                return [], status
            successors, chain_status = self._capture_key_successors(
                values,
                captures,
                allow_promotions,
            )
            return successors, status | chain_status

        quiet = self._matching_indices(
            black,
            white,
            kings,
            black_to_move,
            False,
            True,
            None,
        )
        if not quiet:
            return [], TERMINAL

        status = 0
        successors = []
        for index in quiet:
            if self._flags[index] & FLAG_PROMOTION:
                status |= PROMOTION_AVAILABLE
                if not allow_promotions:
                    continue
            successors.append(self._apply_index(*values, index, True))
        return successors, status

    def _expand_value_edges(
        self,
        values: StateKey,
        allow_captures: bool,
        allow_promotions: bool,
    ) -> tuple[list[StateEdge], int]:
        black, white, kings, black_to_move = values
        captures = self._matching_indices(
            black,
            white,
            kings,
            black_to_move,
            True,
            True,
            None,
        )
        if captures:
            status = CAPTURE_AVAILABLE
            if any(self._flags[index] & FLAG_PROMOTION for index in captures):
                status |= PROMOTION_AVAILABLE
            if not allow_captures:
                return [], status
            edges, chain_status = self._capture_key_edges(
                values,
                captures,
                allow_promotions,
                (),
            )
            return edges, status | chain_status

        quiet = self._matching_indices(
            black,
            white,
            kings,
            black_to_move,
            False,
            True,
            None,
        )
        if not quiet:
            return [], TERMINAL

        status = 0
        edges = []
        for index in quiet:
            if self._flags[index] & FLAG_PROMOTION:
                status |= PROMOTION_AVAILABLE
                if not allow_promotions:
                    continue
            edges.append(((index,), self._apply_index(*values, index, True)))
        return edges, status

    def legal_transitions(self, turn: object) -> tuple[Transition, ...]:
        """Return completed legal turns, expanding all mandatory capture chains."""

        turn = as_turn(turn)
        indexes = self.legal_rule_indices(turn)
        if not indexes:
            return ()
        if not self._flags[indexes[0]] & FLAG_CAPTURE:
            return tuple(Transition((index,), self.apply_rule(turn, index)) for index in indexes)

        transitions = []
        for index in indexes:
            primitive_turn = self.apply_rule(turn, index, switch_side=False)
            if self._flags[index] & FLAG_PROMOTION:
                transitions.append(Transition((index,), _finish_turn(primitive_turn)))
            else:
                transitions.extend(self._capture_transitions(primitive_turn, self._is_empty[index], (index,)))
        return tuple(transitions)

    def successors(self, turn: object) -> tuple[Turn, ...]:
        """Return successor turns after complete legal moves."""

        return tuple(transition.turn for transition in self.legal_transitions(turn))

    def successor_map(self, turn: object) -> dict[RulePath, Turn]:
        """Return ``{rule_index_path: successor_turn}`` for complete legal moves."""

        return {transition.rule_indices: transition.turn for transition in self.legal_transitions(turn)}

    def _capture_transitions(self, turn: Turn, from_mask: int, path: RulePath) -> list[Transition]:
        indexes = self.matching_rule_indices(turn, capture=True, from_mask=from_mask)
        if not indexes:
            return [Transition(path, _finish_turn(turn))]

        transitions = []
        for index in indexes:
            primitive_turn = self.apply_rule(turn, index, switch_side=False)
            next_path = (*path, index)
            if self._flags[index] & FLAG_PROMOTION:
                transitions.append(Transition(next_path, _finish_turn(primitive_turn)))
            else:
                transitions.extend(self._capture_transitions(primitive_turn, self._is_empty[index], next_path))
        return transitions

    def _matching_indices(
        self,
        black: int,
        white: int,
        kings: int,
        black_to_move: int,
        capture: bool | None,
        allow_promotions: bool,
        from_mask: int | None,
    ) -> list[int]:
        empty = (~(black | white)) & MASK32
        moving_kings = kings & (black if black_to_move else white)
        rows = self._candidate_rows[
            _runtime_candidate_key(
                black_to_move,
                bool(moving_kings),
                allow_promotions,
                capture,
            )
        ]
        matches = []
        for index, is_black, is_white, is_empty, flags, mover in rows:
            if from_mask is not None and mover != from_mask:
                continue
            if black & is_black != is_black or white & is_white != is_white or empty & is_empty != is_empty:
                continue
            mover_is_king = kings & mover
            if flags & FLAG_KING and not mover_is_king:
                continue
            if flags & FLAG_PROMOTION and mover_is_king:
                continue
            matches.append(index)
        return matches

    def _has_matching(
        self,
        black: int,
        white: int,
        kings: int,
        black_to_move: int,
        capture: bool | None,
        allow_promotions: bool,
        from_mask: int | None,
    ) -> bool:
        empty = (~(black | white)) & MASK32
        moving_kings = kings & (black if black_to_move else white)
        rows = self._candidate_rows[
            _runtime_candidate_key(
                black_to_move,
                bool(moving_kings),
                allow_promotions,
                capture,
            )
        ]
        for _index, is_black, is_white, is_empty, flags, mover in rows:
            if from_mask is not None and mover != from_mask:
                continue
            if black & is_black != is_black or white & is_white != is_white or empty & is_empty != is_empty:
                continue
            mover_is_king = kings & mover
            if flags & FLAG_KING and not mover_is_king:
                continue
            if flags & FLAG_PROMOTION and mover_is_king:
                continue
            return True
        return False

    def _index_applies(
        self,
        black: int,
        white: int,
        kings: int,
        black_to_move: int,
        index: int,
    ) -> bool:
        return _rule_applies_values(
            black,
            white,
            kings,
            black_to_move,
            self._is_black[index],
            self._is_white[index],
            self._is_empty[index],
            self._flags[index],
        )

    def _apply_index(
        self,
        black: int,
        white: int,
        kings: int,
        black_to_move: int,
        index: int,
        switch_side: bool,
    ) -> StateKey:
        is_black = self._is_black[index]
        is_white = self._is_white[index]
        destination = self._is_empty[index]
        cleared = is_black | is_white
        if black_to_move:
            next_black = (black & ~cleared) | destination
            next_white = white & ~cleared
            mover = is_black
        else:
            next_black = black & ~cleared
            next_white = (white & ~cleared) | destination
            mover = is_white
        next_kings = kings & ~cleared
        if kings & mover or self._flags[index] & FLAG_PROMOTION:
            next_kings |= destination
        next_side = 1 - black_to_move if switch_side else black_to_move
        return next_black, next_white, next_kings, next_side

    def _capture_key_successors(
        self,
        values: StateKey,
        indexes: list[int],
        allow_promotions: bool,
    ) -> tuple[list[StateKey], int]:
        black_to_move = values[3]
        successors = []
        status = 0
        for index in indexes:
            primitive = self._apply_index(*values, index, False)
            if self._flags[index] & FLAG_PROMOTION:
                status |= PROMOTION_AVAILABLE
                if allow_promotions:
                    successors.append((*primitive[:3], 1 - black_to_move))
                continue

            next_captures = self._matching_indices(
                *primitive,
                True,
                True,
                self._is_empty[index],
            )
            if next_captures:
                nested, nested_status = self._capture_key_successors(
                    primitive,
                    next_captures,
                    allow_promotions,
                )
                successors.extend(nested)
                status |= nested_status
            else:
                successors.append((*primitive[:3], 1 - black_to_move))
        return successors, status

    def _capture_key_edges(
        self,
        values: StateKey,
        indexes: list[int],
        allow_promotions: bool,
        path: RulePath,
    ) -> tuple[list[StateEdge], int]:
        black_to_move = values[3]
        edges = []
        status = 0
        for index in indexes:
            primitive = self._apply_index(*values, index, False)
            next_path = (*path, index)
            if self._flags[index] & FLAG_PROMOTION:
                status |= PROMOTION_AVAILABLE
                if allow_promotions:
                    edges.append((next_path, (*primitive[:3], 1 - black_to_move)))
                continue

            next_captures = self._matching_indices(
                *primitive,
                True,
                True,
                self._is_empty[index],
            )
            if next_captures:
                nested, nested_status = self._capture_key_edges(
                    primitive,
                    next_captures,
                    allow_promotions,
                    next_path,
                )
                edges.extend(nested)
                status |= nested_status
            else:
                edges.append((next_path, (*primitive[:3], 1 - black_to_move)))
        return edges, status

    def _mover(self, index: int) -> int:
        return self._is_black[index] if self._flags[index] & FLAG_BLACK_TO_MOVE else self._is_white[index]

    def _check_index(self, rule_index: object) -> int:
        index = _integer(rule_index, "rule index")
        if not (0 <= index < len(self)):
            raise IndexError(f"rule index out of range: {rule_index!r}")
        return index

rules property

rules: tuple[Rule, ...]

Return the immutable primitive-rule tuple.

records property

records: tuple[RuleRecord, ...]

Return independent plain dictionaries for all primitive rules.

record_map property

record_map: dict[int, RuleRecord]

Return an independent {rule_index: record} dictionary.

rule_set property

rule_set: set[RuleKey]

Return an independent set of hashable primitive-rule tuples.

buffers property

buffers: dict[str, memoryview]

Return immutable typed snapshots of the packed runtime columns.

rules_by_side property

rules_by_side: dict[int, tuple[int, ...]]

Return an independent side-to-rule-index mapping.

rules_by_metadata property

rules_by_metadata: dict[RuleMetadataKey, tuple[int, ...]]

Return an independent metadata-to-rule-index mapping.

american classmethod

american(side: object = 'both') -> Ruleset

Return the standard American checkers primitive ruleset.

Source code in src/clatsop/ruleset.py
@classmethod
def american(cls, side: object = "both") -> Ruleset:
    """Return the standard American checkers primitive ruleset."""

    if side is None or str(side).lower() in ("both", "all"):
        return american_ruleset()
    return cls.from_board_geometry(side=side)

from_board_geometry classmethod

from_board_geometry(side: object = 'both') -> Ruleset

Build rules directly from the 32 playable-square board geometry.

Source code in src/clatsop/ruleset.py
@classmethod
def from_board_geometry(cls, side: object = "both") -> Ruleset:
    """Build rules directly from the 32 playable-square board geometry."""

    rules = []
    for black_to_move in _runtime_sides(side):
        for template in _compact_move_templates():
            if _template_requires_king(black_to_move, template):
                rules.append(_rule_from_template(template, black_to_move, king=1))
            else:
                rules.append(_rule_from_template(template, black_to_move, king=0))
                if _template_promotes(black_to_move, template):
                    rules.append(_rule_from_template(template, black_to_move, king=1))
    return cls(rules)

from_records classmethod

from_records(records: object) -> Ruleset

Build a ruleset from flat records, rule objects, or a dataframe.

Source code in src/clatsop/ruleset.py
@classmethod
def from_records(cls, records: object) -> Ruleset:
    """Build a ruleset from flat records, rule objects, or a dataframe."""

    return cls(_records_from_any(records))

record

record(rule_index: object) -> RuleRecord

Return one rule as a plain dictionary.

Source code in src/clatsop/ruleset.py
def record(self, rule_index: object) -> RuleRecord:
    """Return one rule as a plain dictionary."""

    index = self._check_index(rule_index)
    return _indexed_record(index, self[index])

to_dataframe

to_dataframe() -> Any

Return three stored masks and four unpacked metadata bits.

Source code in src/clatsop/ruleset.py
def to_dataframe(self) -> Any:
    """Return three stored masks and four unpacked metadata bits."""

    import pandas as pd

    df = pd.DataFrame.from_records(self.records, columns=("rule_index", *RULE_COLUMNS))
    return df.set_index("rule_index").rename_axis(None)

select_records

select_records(rules: object | None = None, *, black_to_move: object | None = None, king: object | None = None, promotion: object | None = None, capture: object | None = None, sort_by: str | Iterable[str] | None = None, reverse: bool = False) -> list[RuleRecord]

Return filtered and sorted rule records from common selection inputs.

Source code in src/clatsop/ruleset.py
def select_records(
    self,
    rules: object | None = None,
    *,
    black_to_move: object | None = None,
    king: object | None = None,
    promotion: object | None = None,
    capture: object | None = None,
    sort_by: str | Iterable[str] | None = None,
    reverse: bool = False,
) -> list[RuleRecord]:
    """Return filtered and sorted rule records from common selection inputs."""

    black_to_move, king, promotion, capture = _rule_filter_values(
        black_to_move,
        king,
        promotion,
        capture,
    )
    records = None
    if rules is None:
        records = [dict(record) for record in self.records]
    elif isinstance(rules, int):
        records = [self.record(rules)]
    elif isinstance(rules, slice):
        records = [self.record(index) for index in range(len(self))[rules]]
    elif hasattr(rules, "to_dict") and hasattr(rules, "columns"):
        rows = []
        dataframe = cast(Any, rules)
        for index, record in dataframe.to_dict("index").items():
            record = dict(record)
            record.setdefault("rule_index", index)
            rows.append(record)
        records = rows
    else:
        values = list(cast(Iterable[object], rules))
        if all(isinstance(value, int) for value in values):
            records = [self.record(index) for index in values]
        else:
            records = []
            for value in values:
                if isinstance(value, Rule):
                    key = value.as_tuple()
                    if key not in self._rule_indices:
                        raise ValueError("selected rule does not belong to this ruleset")
                    records.append(_indexed_record(self._rule_indices[key], value))
                else:
                    records.append(dict(cast(Mapping[str, int], value)))

    return _query_rule_records(
        records,
        black_to_move=black_to_move,
        king=king,
        promotion=promotion,
        capture=capture,
        sort_by=sort_by,
        reverse=reverse,
    )

plot

plot(rules: object | None = None, size: float = 3.2, title: str | None = None, show: bool = True, *, black_to_move: object | None = None, king: object | None = None, promotion: object | None = None, capture: object | None = None, sort_by: str | Iterable[str] | None = None, reverse: bool = False) -> list[tuple[Any, Any]]

Plot selected rules with condition, effect, and summary boards.

Source code in src/clatsop/ruleset.py
def plot(
    self,
    rules: object | None = None,
    size: float = 3.2,
    title: str | None = None,
    show: bool = True,
    *,
    black_to_move: object | None = None,
    king: object | None = None,
    promotion: object | None = None,
    capture: object | None = None,
    sort_by: str | Iterable[str] | None = None,
    reverse: bool = False,
) -> list[tuple[Any, Any]]:
    """Plot selected rules with condition, effect, and summary boards."""

    from .display import show_ruleset_rows

    selected = self.select_records(
        rules,
        black_to_move=black_to_move,
        king=king,
        promotion=promotion,
        capture=capture,
        sort_by=sort_by,
        reverse=reverse,
    )
    return show_ruleset_rows(selected, size=size, title=title, show=show)

display

display(rules: object | None = None, size: float = 3.2, title: str | None = None, show: bool = True, *, black_to_move: object | None = None, king: object | None = None, promotion: object | None = None, capture: object | None = None, sort_by: str | Iterable[str] | None = None, reverse: bool = False) -> list[tuple[Any, Any]]

Render selected rules with condition, effect, and summary boards.

Source code in src/clatsop/ruleset.py
def display(
    self,
    rules: object | None = None,
    size: float = 3.2,
    title: str | None = None,
    show: bool = True,
    *,
    black_to_move: object | None = None,
    king: object | None = None,
    promotion: object | None = None,
    capture: object | None = None,
    sort_by: str | Iterable[str] | None = None,
    reverse: bool = False,
) -> list[tuple[Any, Any]]:
    """Render selected rules with condition, effect, and summary boards."""

    return self.plot(
        rules=rules,
        size=size,
        title=title,
        show=show,
        black_to_move=black_to_move,
        king=king,
        promotion=promotion,
        capture=capture,
        sort_by=sort_by,
        reverse=reverse,
    )

indices_for

indices_for(black_to_move: object | None = None, king: object | None = None, promotion: object | None = None, capture: object | None = None, *, sort_by: str | Iterable[str] | None = None, reverse: bool = False) -> list[int]

Return rule indexes matching metadata filters.

Source code in src/clatsop/ruleset.py
def indices_for(
    self,
    black_to_move: object | None = None,
    king: object | None = None,
    promotion: object | None = None,
    capture: object | None = None,
    *,
    sort_by: str | Iterable[str] | None = None,
    reverse: bool = False,
) -> list[int]:
    """Return rule indexes matching metadata filters."""

    black_to_move, king, promotion, capture = _rule_filter_values(
        black_to_move,
        king,
        promotion,
        capture,
    )
    if all(value is not None for value in (black_to_move, king, promotion, capture)):
        key = (
            black_to_move,
            king,
            promotion,
            capture,
        )
        indexes = list(self._rules_by_metadata.get(key, ()))
    else:
        indexes = []
        for key, key_indexes in self._rules_by_metadata.items():
            if all(
                requested is None or value == requested
                for value, requested in zip(key, (black_to_move, king, promotion, capture), strict=True)
            ):
                indexes.extend(key_indexes)
        indexes.sort()

    if sort_by is None:
        return indexes
    records = _sort_rule_records([self.record(index) for index in indexes], sort_by, reverse)
    return [record["rule_index"] for record in records]

rules_for

rules_for(*, black_to_move: object | None = None, king: object | None = None, promotion: object | None = None, capture: object | None = None, sort_by: str | Iterable[str] | None = None, reverse: bool = False) -> tuple[Rule, ...]

Return rule objects matching metadata filters in the requested order.

Source code in src/clatsop/ruleset.py
def rules_for(
    self,
    *,
    black_to_move: object | None = None,
    king: object | None = None,
    promotion: object | None = None,
    capture: object | None = None,
    sort_by: str | Iterable[str] | None = None,
    reverse: bool = False,
) -> tuple[Rule, ...]:
    """Return rule objects matching metadata filters in the requested order."""

    indexes = self.indices_for(
        black_to_move,
        king,
        promotion,
        capture,
        sort_by=sort_by,
        reverse=reverse,
    )
    return tuple(self[index] for index in indexes)

matching_rule_indices

matching_rule_indices(turn: object, *, capture: object | None = None, allow_promotions: object = True, from_mask: object | None = None) -> list[int]

Return bitwise-matched rule indexes under runtime policy filters.

Source code in src/clatsop/ruleset.py
def matching_rule_indices(
    self,
    turn: object,
    *,
    capture: object | None = None,
    allow_promotions: object = True,
    from_mask: object | None = None,
) -> list[int]:
    """Return bitwise-matched rule indexes under runtime policy filters."""

    turn = as_turn(turn)
    capture = _optional_flag_bit(capture, "capture")
    capture_mode = None if capture is None else bool(capture)
    allow_promotions = bool(_flag_bit(allow_promotions, "allow_promotions"))
    if from_mask is not None:
        from_mask = _mask32(from_mask, "from_mask")
    return self._matching_indices(
        turn.black,
        turn.white,
        turn.kings,
        turn.black_to_move,
        capture_mode,
        allow_promotions,
        from_mask,
    )

matching_rules

matching_rules(turn: object, *, capture: object | None = None, allow_promotions: object = True, from_mask: object | None = None) -> tuple[Rule, ...]

Return matching rule objects.

Source code in src/clatsop/ruleset.py
def matching_rules(
    self,
    turn: object,
    *,
    capture: object | None = None,
    allow_promotions: object = True,
    from_mask: object | None = None,
) -> tuple[Rule, ...]:
    """Return matching rule objects."""

    indexes = self.matching_rule_indices(
        turn,
        capture=capture,
        allow_promotions=allow_promotions,
        from_mask=from_mask,
    )
    return tuple(self[index] for index in indexes)

legal_rule_indices

legal_rule_indices(turn: object, from_mask: object | None = None) -> list[int]

Return legal primitive rule indexes, applying mandatory capture.

Source code in src/clatsop/ruleset.py
def legal_rule_indices(self, turn: object, from_mask: object | None = None) -> list[int]:
    """Return legal primitive rule indexes, applying mandatory capture."""

    turn = as_turn(turn)
    captures = self._matching_indices(
        turn.black,
        turn.white,
        turn.kings,
        turn.black_to_move,
        True,
        True,
        None,
    )
    legal = captures or self._matching_indices(
        turn.black,
        turn.white,
        turn.kings,
        turn.black_to_move,
        False,
        True,
        None,
    )
    if from_mask is None:
        return legal
    from_mask = _mask32(from_mask, "from_mask")
    return [index for index in legal if self._mover(index) == from_mask]

legal_rules

legal_rules(turn: object, from_mask: object | None = None) -> tuple[Rule, ...]

Return legal primitive rule objects.

Source code in src/clatsop/ruleset.py
def legal_rules(self, turn: object, from_mask: object | None = None) -> tuple[Rule, ...]:
    """Return legal primitive rule objects."""

    return tuple(self[index] for index in self.legal_rule_indices(turn, from_mask=from_mask))

has_capture

has_capture(turn: object, from_mask: object | None = None) -> bool

Return whether the side to move has an immediately legal capture.

Source code in src/clatsop/ruleset.py
def has_capture(self, turn: object, from_mask: object | None = None) -> bool:
    """Return whether the side to move has an immediately legal capture."""

    black, white, kings, black_to_move = _turn_values(turn)
    if from_mask is not None:
        from_mask = _mask32(from_mask, "from_mask")
    return self._has_matching(
        black,
        white,
        kings,
        black_to_move,
        True,
        True,
        from_mask,
    )

has_promotion

has_promotion(turn: object) -> bool

Return whether any legal first-step move promotes a man.

Source code in src/clatsop/ruleset.py
def has_promotion(self, turn: object) -> bool:
    """Return whether any legal first-step move promotes a man."""

    return any(self._flags[index] & FLAG_PROMOTION for index in self.legal_rule_indices(turn))

apply_rule

apply_rule(turn: object, rule_index: object, switch_side: bool = True) -> Turn

Apply one satisfied primitive rule to a turn.

Source code in src/clatsop/ruleset.py
def apply_rule(
    self,
    turn: object,
    rule_index: object,
    switch_side: bool = True,
) -> Turn:
    """Apply one satisfied primitive rule to a turn."""

    turn = as_turn(turn)
    index = self._check_index(rule_index)
    values = turn.as_tuple()
    if not self._index_applies(*values, index):
        raise ValueError("rule conditions are not satisfied by the turn")
    black, white, kings, black_to_move = self._apply_index(*values, index, switch_side)
    return Turn.from_masks(black, white, kings, black_to_move, turn.metadata)

apply_key

apply_key(turn: object, rule_index: object, switch_side: bool = True) -> StateKey

Apply one rule and return a native four-integer state key.

Source code in src/clatsop/ruleset.py
def apply_key(
    self,
    turn: object,
    rule_index: object,
    switch_side: bool = True,
) -> StateKey:
    """Apply one rule and return a native four-integer state key."""

    values = _turn_values(turn)
    index = self._check_index(rule_index)
    if not self._index_applies(*values, index):
        raise ValueError("rule conditions are not satisfied by the turn")
    return self._apply_index(*values, index, switch_side)

apply_packed

apply_packed(state: object, rule_index: object, switch_side: bool = True) -> PackedState

Apply one rule to a packed state and return a packed successor.

Source code in src/clatsop/ruleset.py
def apply_packed(
    self,
    state: object,
    rule_index: object,
    switch_side: bool = True,
) -> PackedState:
    """Apply one rule to a packed state and return a packed successor."""

    values = unpack_state_values(state)
    index = self._check_index(rule_index)
    if not self._index_applies(*values, index):
        raise ValueError("rule conditions are not satisfied by the turn")
    return pack_validated_state(*self._apply_index(*values, index, switch_side))

expand_keys

expand_keys(turn: object, *, allow_captures: bool = True, allow_promotions: bool = True) -> tuple[tuple[StateKey, ...], int]

Return legal successor keys and packed capture/promotion/terminal status.

Source code in src/clatsop/ruleset.py
def expand_keys(
    self,
    turn: object,
    *,
    allow_captures: bool = True,
    allow_promotions: bool = True,
) -> tuple[tuple[StateKey, ...], int]:
    """Return legal successor keys and packed capture/promotion/terminal status."""

    values = _turn_values(turn)
    successors, status = self._expand_values(values, allow_captures, allow_promotions)
    return tuple(successors), status

expand_packed

expand_packed(state: object, *, allow_captures: bool = True, allow_promotions: bool = True) -> tuple[tuple[PackedState, ...], int]

Expand one packed state without allocating board or turn objects.

Source code in src/clatsop/ruleset.py
def expand_packed(
    self,
    state: object,
    *,
    allow_captures: bool = True,
    allow_promotions: bool = True,
) -> tuple[tuple[PackedState, ...], int]:
    """Expand one packed state without allocating board or turn objects."""

    values = unpack_state_values(state)
    successors, status = self._expand_values(values, allow_captures, allow_promotions)
    return tuple(pack_validated_state(*successor) for successor in successors), status

expand_packed_unchecked

expand_packed_unchecked(state: PackedState, *, allow_captures: bool = True, allow_promotions: bool = True) -> tuple[tuple[PackedState, ...], int]

Expand a trusted packed state without repeating invariant checks.

Source code in src/clatsop/ruleset.py
def expand_packed_unchecked(
    self,
    state: PackedState,
    *,
    allow_captures: bool = True,
    allow_promotions: bool = True,
) -> tuple[tuple[PackedState, ...], int]:
    """Expand a trusted packed state without repeating invariant checks."""

    values = unpack_validated_state(state)
    successors, status = self._expand_values(values, allow_captures, allow_promotions)
    return tuple(pack_validated_state(*successor) for successor in successors), status

expand_edges

expand_edges(turn: object, *, allow_captures: bool = True, allow_promotions: bool = True) -> tuple[tuple[StateEdge, ...], int]

Return completed rule paths paired with native successor keys.

Source code in src/clatsop/ruleset.py
def expand_edges(
    self,
    turn: object,
    *,
    allow_captures: bool = True,
    allow_promotions: bool = True,
) -> tuple[tuple[StateEdge, ...], int]:
    """Return completed rule paths paired with native successor keys."""

    values = _turn_values(turn)
    edges, status = self._expand_value_edges(values, allow_captures, allow_promotions)
    return tuple(edges), status

expand_packed_edges

expand_packed_edges(state: object, *, allow_captures: bool = True, allow_promotions: bool = True) -> tuple[tuple[PackedStateEdge, ...], int]

Return completed rule paths paired with packed successor states.

Source code in src/clatsop/ruleset.py
def expand_packed_edges(
    self,
    state: object,
    *,
    allow_captures: bool = True,
    allow_promotions: bool = True,
) -> tuple[tuple[PackedStateEdge, ...], int]:
    """Return completed rule paths paired with packed successor states."""

    values = unpack_state_values(state)
    edges, status = self._expand_value_edges(values, allow_captures, allow_promotions)
    return tuple((path, pack_validated_state(*successor)) for path, successor in edges), status

legal_transitions

legal_transitions(turn: object) -> tuple[Transition, ...]

Return completed legal turns, expanding all mandatory capture chains.

Source code in src/clatsop/ruleset.py
def legal_transitions(self, turn: object) -> tuple[Transition, ...]:
    """Return completed legal turns, expanding all mandatory capture chains."""

    turn = as_turn(turn)
    indexes = self.legal_rule_indices(turn)
    if not indexes:
        return ()
    if not self._flags[indexes[0]] & FLAG_CAPTURE:
        return tuple(Transition((index,), self.apply_rule(turn, index)) for index in indexes)

    transitions = []
    for index in indexes:
        primitive_turn = self.apply_rule(turn, index, switch_side=False)
        if self._flags[index] & FLAG_PROMOTION:
            transitions.append(Transition((index,), _finish_turn(primitive_turn)))
        else:
            transitions.extend(self._capture_transitions(primitive_turn, self._is_empty[index], (index,)))
    return tuple(transitions)

successors

successors(turn: object) -> tuple[Turn, ...]

Return successor turns after complete legal moves.

Source code in src/clatsop/ruleset.py
def successors(self, turn: object) -> tuple[Turn, ...]:
    """Return successor turns after complete legal moves."""

    return tuple(transition.turn for transition in self.legal_transitions(turn))

successor_map

successor_map(turn: object) -> dict[RulePath, Turn]

Return {rule_index_path: successor_turn} for complete legal moves.

Source code in src/clatsop/ruleset.py
def successor_map(self, turn: object) -> dict[RulePath, Turn]:
    """Return ``{rule_index_path: successor_turn}`` for complete legal moves."""

    return {transition.rule_indices: transition.turn for transition in self.legal_transitions(turn)}

Validate and pack a turn-like value into one 97-bit integer.

Source code in src/clatsop/ruleset.py
def pack_state(turn: object) -> PackedState:
    """Validate and pack a turn-like value into one 97-bit integer."""

    return pack_state_values(*_turn_values(turn))

Validate and unpack one 97-bit integer into a native state key.

Source code in src/clatsop/ruleset.py
def unpack_state(state: object) -> StateKey:
    """Validate and unpack one 97-bit integer into a native state key."""

    return unpack_state_values(state)

Return a no-capture, no-promotion state's unique layer from the start.

Source code in src/clatsop/ruleset.py
def quiet_ply(turn: object) -> int:
    """Return a no-capture, no-promotion state's unique layer from the start."""

    return quiet_ply_values(*_turn_values(turn))

Native 32-bit playable-square encodings for checkers boards.

is_playable_square

is_playable_square(row: object, col: object) -> bool

Return whether (row, col) is one of the 32 playable board squares.

Source code in src/clatsop/encoding.py
def is_playable_square(row: object, col: object) -> bool:
    """Return whether ``(row, col)`` is one of the 32 playable board squares."""

    try:
        row = integer_index(cast(SupportsIndex, row))
        col = integer_index(cast(SupportsIndex, col))
    except TypeError:
        return False
    return 0 <= row < 8 and 0 <= col < 8 and (row + col) % 2 == 1

square_index32

square_index32(row: object, col: object) -> int

Return the 0-based playable-square index for (row, col).

Source code in src/clatsop/encoding.py
def square_index32(row: object, col: object) -> int:
    """Return the 0-based playable-square index for ``(row, col)``."""

    try:
        row = integer_index(cast(SupportsIndex, row))
        col = integer_index(cast(SupportsIndex, col))
    except TypeError as error:
        raise ValueError(f"square coordinates must be integers: {(row, col)}") from error
    if not is_playable_square(row, col):
        raise ValueError(f"square is not playable: {(row, col)}")
    return row * 4 + (col // 2)

square_mask32

square_mask32(row: object, col: object) -> int

Return a one-bit 32-bit mask for a playable board square.

Source code in src/clatsop/encoding.py
def square_mask32(row: object, col: object) -> int:
    """Return a one-bit 32-bit mask for a playable board square."""

    return 1 << square_index32(row, col)

square_coords32

square_coords32(square: object) -> tuple[int, int]

Return (row, col) for a playable-square index.

Source code in src/clatsop/encoding.py
def square_coords32(square: object) -> tuple[int, int]:
    """Return ``(row, col)`` for a playable-square index."""

    try:
        square = integer_index(cast(SupportsIndex, square))
    except TypeError as error:
        raise ValueError(f"square must be an integer: {square!r}") from error
    if not (0 <= square < 32):
        raise ValueError(f"square out of range: {square!r}")
    row, offset = divmod(square, 4)
    col = 2 * offset + (1 if row % 2 == 0 else 0)
    return row, col

square_from_mask32

square_from_mask32(mask: object) -> tuple[int, int]

Return (row, col) for a one-bit 32-bit playable-square mask.

Source code in src/clatsop/encoding.py
def square_from_mask32(mask: object) -> tuple[int, int]:
    """Return ``(row, col)`` for a one-bit 32-bit playable-square mask."""

    try:
        mask = integer_index(cast(SupportsIndex, mask))
    except TypeError as error:
        raise ValueError(f"mask must be an integer: {mask!r}") from error
    if mask <= 0 or mask & (mask - 1):
        raise ValueError("mask must contain exactly one bit")
    square = mask.bit_length() - 1
    if square >= 32:
        raise ValueError("mask is outside the playable-square board")
    return square_coords32(square)

playable_squares

playable_squares() -> tuple[tuple[int, int, int], ...]

Return all playable squares as (row, col, mask) tuples.

Source code in src/clatsop/encoding.py
def playable_squares() -> tuple[tuple[int, int, int], ...]:
    """Return all playable squares as ``(row, col, mask)`` tuples."""

    return tuple(
        (row, col, square_mask32(row, col)) for row in range(8) for col in range(8) if is_playable_square(row, col)
    )

Notebook-friendly visualization helpers for native 32-bit boards and rules.

show_board

show_board(board: object, size: float = 3, title: str | None = None, show: bool = True) -> tuple[Any, Any]

Render a board or turn.

Source code in src/clatsop/display.py
def show_board(
    board: object,
    size: float = 3,
    title: str | None = None,
    show: bool = True,
) -> tuple[Any, Any]:
    """Render a board or turn."""

    import matplotlib.pyplot as plt

    if isinstance(board, Turn):
        turn = board
        board = turn.board
        title = title or f"{turn.side} to move"
    else:
        board = as_board(board)

    fig, ax = plt.subplots(1, 1, figsize=(size, size))
    draw_board(ax, board, title=title)
    if show:
        plt.show()
    return fig, ax

show_turn

show_turn(turn: object, size: float = 3, title: str | None = None, show: bool = True) -> tuple[Any, Any]

Render a turn.

Source code in src/clatsop/display.py
def show_turn(
    turn: object,
    size: float = 3,
    title: str | None = None,
    show: bool = True,
) -> tuple[Any, Any]:
    """Render a turn."""

    return show_board(as_turn(turn), size=size, title=title, show=show)

show_ruleset_rows

show_ruleset_rows(ruleset: object, rules: object | None = None, size: float = 3.2, title: str | None = None, show: bool = True) -> list[tuple[Any, Any]]

Render selected rules as separate condition/effect/summary rows.

Source code in src/clatsop/display.py
def show_ruleset_rows(
    ruleset: object,
    rules: object | None = None,
    size: float = 3.2,
    title: str | None = None,
    show: bool = True,
) -> list[tuple[Any, Any]]:
    """Render selected rules as separate condition/effect/summary rows."""

    rows = _rule_records(ruleset, rules)
    if not rows:
        raise ValueError("at least one rule row is required")

    figures = []
    for row in rows:
        fig, axes = _show_rule_row(row, size=size, title=_rule_row_title(row, title))
        figures.append((fig, axes))
        if show:
            _display_figure(fig)
            _close_figure(fig)
    return figures

draw_board

draw_board(ax: Any, board: object, title: str | None = None, dots: object = 0) -> Any

Draw a 32-bit board on an existing matplotlib axis.

Source code in src/clatsop/display.py
def draw_board(
    ax: Any,
    board: object,
    title: str | None = None,
    dots: object = 0,
) -> Any:
    """Draw a 32-bit board on an existing matplotlib axis."""

    board = as_board(board)
    _draw_base_board(ax)
    _draw_mask_markers(ax, dots, "empty")
    _draw_mask_markers(ax, board.white, "white")
    _draw_mask_markers(ax, board.black, "black")
    _draw_mask_markers(ax, board.kings, "king_overlay")
    if title:
        ax.set_title(title)
    return ax