Skip to content

bitbully_core

Low-level pybind11 bindings for BitBully (Connect-4 solver).

This module exposes the core C++ engine via pybind11. It provides:

  • class BoardCore: Fast bitboard-based Connect-4 position representation.
  • class BitBullyCore : Perfect-play solver (MTD(f), negamax, null-window) with optional opening book.
  • class OpeningBookCore: Opening book reader / lookup helper.
  • enum Player: Player enum used by the engine.
  • data N_COLUMNS, data N_ROWS: Board dimensions (standard Connect-4: 7x6).
Notes

These APIs are low-level and mirror the underlying C++ engine closely. Most users should prefer the high-level Python wrapper (e.g. bitbully.Board, bitbully.BitBully) unless they need maximum control or performance.

Example

Create a board, score moves, and pick a best move:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play("334411")
assert isinstance(str(board), str) and str(board) != ""

solver = bbc.BitBullyCore()
scores = solver.scoreMoves(board)

# One score per column.
assert len(scores) == 7

# Pick best column by score (ties resolved by first max).
best_col = max(range(7), key=scores.__getitem__)
assert 0 <= best_col < 7

Classes:

Name Description
BitBullyCore

Perfect-play Connect-4 solver implemented in C++.

BoardCore

Low-level Connect-4 board representation (bitboard-based).

OpeningBookCore

Opening book reader and lookup helper.

Player

Player identifiers used by the engine.

Attributes:

Name Type Description
N_COLUMNS int

Number of columns of the standard Connect-4 board (7).

N_ROWS int

Number of rows of the standard Connect-4 board (6).

N_COLUMNS module-attribute

N_COLUMNS: int

Number of columns of the standard Connect-4 board (7).

Example

Read the board dimensions:

import bitbully.bitbully_core as bbc

assert bbc.N_COLUMNS == 7
assert bbc.N_ROWS == 6

N_ROWS module-attribute

N_ROWS: int

Number of rows of the standard Connect-4 board (6).

Example

Read the board dimensions:

import bitbully.bitbully_core as bbc

assert bbc.N_COLUMNS == 7
assert bbc.N_ROWS == 6

__all__ module-attribute

__all__: list[str] = ['N_COLUMNS', 'N_ROWS', 'BitBullyCore', 'BoardCore', 'OpeningBookCore', 'Player']

BitBullyCore

Perfect-play Connect-4 solver implemented in C++.

The solver evaluates positions from the perspective of the side to move. It supports multiple search methods and optional opening-book acceleration.

Notes
  • Column indices are 0..6 (left to right).
  • Scores are engine-defined integers; higher is better for the player to move.
  • When an opening book is loaded, early-game positions can be evaluated in constant time.
Example

Score all legal moves in a position:

import bitbully.bitbully_core as bbc
import bitbully_databases as bbd
db_path = bbd.BitBullyDatabases.get_database_path("default")

board = bbc.BoardCore()
assert board.play(6 * "3")

solver = bbc.BitBullyCore(db_path)
scores = solver.scoreMoves(board)

# Scores has length 7 (one per column).
assert len(scores) == 7

# Pick best column by score.
best_col = max(range(7), key=scores.__getitem__)
print("Best column:", best_col)
Expected output:
    Best column: 2

Methods:

Name Description
getNodeCounter

Return the number of visited nodes since the last reset.

isBookLoaded

Return whether an opening book is currently loaded.

loadBook

Load an opening book from a file path.

mtdf

Evaluate a position using the MTD(f) algorithm.

negamax

Evaluate a position using negamax (alpha-beta) search.

nullWindow

Evaluate a position using a null-window search.

resetBook

Unload the currently loaded opening book (if any).

resetNodeCounter

Reset the internal node counter.

resetTranspositionTable

Clear the internal transposition table.

scoreMove

Evaluate a single move in the given position.

scoreMoves

Evaluate all columns (0..6) in the given position.

getNodeCounter

getNodeCounter() -> int

Return the number of visited nodes since the last reset.

Returns:

Name Type Description
int int

Number of search nodes visited since the last call to BitBullyCore.resetNodeCounter.

Example

Count how many nodes a search visited:

import bitbully.bitbully_core as bbc

solver = bbc.BitBullyCore()
board = bbc.BoardCore()
assert board.play("333331111555")
assert solver.getNodeCounter() == 0

_ = solver.mtdf(board, first_guess=0)
assert solver.getNodeCounter() > 0

solver.resetNodeCounter()
assert solver.getNodeCounter() == 0

Source code in src/bitbully/bitbully_core.pyi
def getNodeCounter(self) -> int:
    """Return the number of visited nodes since the last reset.

    Returns:
        int: Number of search nodes visited since the last call to
            [BitBullyCore.resetNodeCounter][src.bitbully.bitbully_core.BitBullyCore.resetNodeCounter].

    Example:
        Count how many nodes a search visited:
        ```python
        import bitbully.bitbully_core as bbc

        solver = bbc.BitBullyCore()
        board = bbc.BoardCore()
        assert board.play("333331111555")
        assert solver.getNodeCounter() == 0

        _ = solver.mtdf(board, first_guess=0)
        assert solver.getNodeCounter() > 0

        solver.resetNodeCounter()
        assert solver.getNodeCounter() == 0
        ```
    """

isBookLoaded

isBookLoaded() -> bool

Return whether an opening book is currently loaded.

Returns:

Name Type Description
bool bool

True if a book is loaded, otherwise False.

Example

Check if the solver currently uses an opening book:

import bitbully.bitbully_core as bbc

solver = bbc.BitBullyCore()
assert solver.isBookLoaded() is False

Source code in src/bitbully/bitbully_core.pyi
def isBookLoaded(self) -> bool:
    """Return whether an opening book is currently loaded.

    Returns:
        bool: ``True`` if a book is loaded, otherwise ``False``.

    Example:
        Check if the solver currently uses an opening book:
        ```python
        import bitbully.bitbully_core as bbc

        solver = bbc.BitBullyCore()
        assert solver.isBookLoaded() is False
        ```
    """

loadBook

loadBook(bookPath: PathLike[str] | str = ...) -> bool

Load an opening book from a file path.

Parameters:

Name Type Description Default

bookPath

PathLike[str] | str

Path to the opening book file.

...

Returns:

Name Type Description
bool bool

True if the book was loaded successfully, otherwise False.

Example

Load a book and verify it is active:

import bitbully.bitbully_core as bbc
import bitbully_databases as bbd
db_path = bbd.BitBullyDatabases.get_database_path("default")

solver = bbc.BitBullyCore()
ok = solver.loadBook(db_path)  # replace with your file
if ok:
    assert solver.isBookLoaded() is True

Source code in src/bitbully/bitbully_core.pyi
def loadBook(self, bookPath: os.PathLike[str] | str = ...) -> bool:
    """Load an opening book from a file path.

    Args:
        bookPath (os.PathLike[str] | str): Path to the opening book file.

    Returns:
        bool: ``True`` if the book was loaded successfully, otherwise ``False``.

    Example:
        Load a book and verify it is active:
        ```python
        import bitbully.bitbully_core as bbc
        import bitbully_databases as bbd
        db_path = bbd.BitBullyDatabases.get_database_path("default")

        solver = bbc.BitBullyCore()
        ok = solver.loadBook(db_path)  # replace with your file
        if ok:
            assert solver.isBookLoaded() is True
        ```
    """

mtdf

Evaluate a position using the MTD(f) algorithm.

Parameters:

Name Type Description Default

board

BoardCore

Position to evaluate.

required

first_guess

int

Initial guess for the score (often 0).

required

Returns:

Name Type Description
int int

Evaluation score for the side to move.

Example

Evaluate a position using MTD(f) (six moves in the center column):

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
for _ in range(6):
    assert board.play(3)

solver = bbc.BitBullyCore()
score = solver.mtdf(board, first_guess=0)
print("MTD(f) score:", score)

assert isinstance(score, int)
assert solver.getNodeCounter() > 0
Expected output:
    MTD(f) score: 1

Source code in src/bitbully/bitbully_core.pyi
def mtdf(self, board: "BoardCore", first_guess: int) -> int:
    """Evaluate a position using the MTD(f) algorithm.

    Args:
        board (BoardCore): Position to evaluate.
        first_guess (int): Initial guess for the score (often 0).

    Returns:
        int: Evaluation score for the side to move.

    Example:
        Evaluate a position using MTD(f) (six moves in the center column):
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        for _ in range(6):
            assert board.play(3)

        solver = bbc.BitBullyCore()
        score = solver.mtdf(board, first_guess=0)
        print("MTD(f) score:", score)

        assert isinstance(score, int)
        assert solver.getNodeCounter() > 0
        ```
        Expected output:
        ```text
            MTD(f) score: 1
        ```
    """

negamax

negamax(board: BoardCore, alpha: int, beta: int, depth: int) -> int

Evaluate a position using negamax (alpha-beta) search.

Parameters:

Name Type Description Default

board

BoardCore

Position to evaluate.

required

alpha

int

Alpha bound.

required

beta

int

Beta bound.

required

depth

int

Search depth in plies.

required

Returns:

Name Type Description
int int

Evaluation score for the side to move.

Example

Run a negamax call with an alpha-beta window:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play("334411")

solver = bbc.BitBullyCore()
score = solver.negamax(board, alpha=-1000, beta=1000, depth=0)
print("Negamax score:", score)
Expected output:
    Negamax score: 18

Source code in src/bitbully/bitbully_core.pyi
def negamax(self, board: "BoardCore", alpha: int, beta: int, depth: int) -> int:
    """Evaluate a position using negamax (alpha-beta) search.

    Args:
        board (BoardCore): Position to evaluate.
        alpha (int): Alpha bound.
        beta (int): Beta bound.
        depth (int): Search depth in plies.

    Returns:
        int: Evaluation score for the side to move.

    Example:
        Run a negamax call with an alpha-beta window:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        assert board.play("334411")

        solver = bbc.BitBullyCore()
        score = solver.negamax(board, alpha=-1000, beta=1000, depth=0)
        print("Negamax score:", score)
        ```
        Expected output:
        ```text
            Negamax score: 18
        ```
    """

nullWindow

nullWindow(board: BoardCore) -> int

Evaluate a position using a null-window search.

Parameters:

Name Type Description Default

board

BoardCore

Position to evaluate.

required

Returns:

Name Type Description
int int

Evaluation score for the side to move.

Example

Use null-window search:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play("334411")

solver = bbc.BitBullyCore()
score = solver.nullWindow(board)
print("Null-window score:", score)
Expected output:
    Null-window score: 18

Source code in src/bitbully/bitbully_core.pyi
def nullWindow(self, board: "BoardCore") -> int:
    """Evaluate a position using a null-window search.

    Args:
        board (BoardCore): Position to evaluate.

    Returns:
        int: Evaluation score for the side to move.

    Example:
        Use null-window search:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        assert board.play("334411")

        solver = bbc.BitBullyCore()
        score = solver.nullWindow(board)
        print("Null-window score:", score)
        ```
        Expected output:
        ```text
            Null-window score: 18
        ```
    """

resetBook

resetBook() -> None

Unload the currently loaded opening book (if any).

Example

Unload a book:

import bitbully.bitbully_core as bbc
import bitbully_databases as bbd
db_path = bbd.BitBullyDatabases.get_database_path("default")

solver = bbc.BitBullyCore()
_ = solver.loadBook(db_path)  # replace with your file
assert solver.isBookLoaded() is True
solver.resetBook()
assert solver.isBookLoaded() is False

Source code in src/bitbully/bitbully_core.pyi
def resetBook(self) -> None:
    """Unload the currently loaded opening book (if any).

    Example:
        Unload a book:
        ```python
        import bitbully.bitbully_core as bbc
        import bitbully_databases as bbd
        db_path = bbd.BitBullyDatabases.get_database_path("default")

        solver = bbc.BitBullyCore()
        _ = solver.loadBook(db_path)  # replace with your file
        assert solver.isBookLoaded() is True
        solver.resetBook()
        assert solver.isBookLoaded() is False
        ```
    """

resetNodeCounter

resetNodeCounter() -> None

Reset the internal node counter.

Example

Reset node counter between searches:

import bitbully.bitbully_core as bbc
board = bbc.BoardCore()
assert board.play("333331111555")

solver = bbc.BitBullyCore()
_ = solver.mtdf(board, first_guess=0)

assert solver.getNodeCounter() > 0
solver.resetNodeCounter()
assert solver.getNodeCounter() == 0

Source code in src/bitbully/bitbully_core.pyi
def resetNodeCounter(self) -> None:
    """Reset the internal node counter.

    Example:
        Reset node counter between searches:
        ```python
        import bitbully.bitbully_core as bbc
        board = bbc.BoardCore()
        assert board.play("333331111555")

        solver = bbc.BitBullyCore()
        _ = solver.mtdf(board, first_guess=0)

        assert solver.getNodeCounter() > 0
        solver.resetNodeCounter()
        assert solver.getNodeCounter() == 0
        ```
    """

resetTranspositionTable

resetTranspositionTable() -> None

Clear the internal transposition table.

Example

Clear cached results (useful for benchmarking):

import bitbully.bitbully_core as bbc

solver = bbc.BitBullyCore()
solver.resetTranspositionTable()

Source code in src/bitbully/bitbully_core.pyi
def resetTranspositionTable(self) -> None:
    """Clear the internal transposition table.

    Example:
        Clear cached results (useful for benchmarking):
        ```python
        import bitbully.bitbully_core as bbc

        solver = bbc.BitBullyCore()
        solver.resetTranspositionTable()
        ```
    """

scoreMove

Evaluate a single move in the given position.

Parameters:

Name Type Description Default

board

BoardCore

Current position.

required

column

int

Column index (0-6) of the move to evaluate.

required

first_guess

int

Initial guess for the score (often 0).

required

Returns:

Name Type Description
int int

Evaluation score of playing the move in column.

Example

Score one candidate move:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play("334411")

solver = bbc.BitBullyCore()
score = solver.scoreMove(board, column=3, first_guess=0)
print("Score for column 3:", score)
Expected output:
    Score for column 3: 3

Example

Score one candidate move and verify it matches the per-column score vector:

import bitbully.bitbully_core as bbc

solver = bbc.BitBullyCore()
board = bbc.BoardCore()
assert board.setBoard([3, 4, 1, 1, 0, 2, 2, 2])

scores = solver.scoreMoves(board)

# Column 4 is known to be best in this position.
one = solver.scoreMove(board, column=4, first_guess=0)
assert one == scores[4] == 3

Source code in src/bitbully/bitbully_core.pyi
def scoreMove(self, board: "BoardCore", column: int, first_guess: int) -> int:
    """Evaluate a single move in the given position.

    Args:
        board (BoardCore): Current position.
        column (int): Column index (0-6) of the move to evaluate.
        first_guess (int): Initial guess for the score (often 0).

    Returns:
        int: Evaluation score of playing the move in ``column``.

    Example:
        Score one candidate move:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        assert board.play("334411")

        solver = bbc.BitBullyCore()
        score = solver.scoreMove(board, column=3, first_guess=0)
        print("Score for column 3:", score)
        ```
        Expected output:
        ```text
            Score for column 3: 3
        ```

    Example:
        Score one candidate move and verify it matches the per-column score vector:
        ```python
        import bitbully.bitbully_core as bbc

        solver = bbc.BitBullyCore()
        board = bbc.BoardCore()
        assert board.setBoard([3, 4, 1, 1, 0, 2, 2, 2])

        scores = solver.scoreMoves(board)

        # Column 4 is known to be best in this position.
        one = solver.scoreMove(board, column=4, first_guess=0)
        assert one == scores[4] == 3
        ```
    """

scoreMoves

scoreMoves(board: BoardCore) -> list[int]

Evaluate all columns (0..6) in the given position.

Parameters:

Name Type Description Default

board

BoardCore

Current position.

required

Returns:

Type Description
list[int]

list[int]: A list of length 7 with per-column scores. Illegal moves (full columns) are included and use an engine-defined sentinel value.

Example

Score all moves and pick the best:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play("334411")

solver = bbc.BitBullyCore()
scores = solver.scoreMoves(board)
assert len(scores) == 7

best_col = max(range(7), key=scores.__getitem__)
assert 0 <= best_col < 7
assert board.isLegalMove(best_col) is True

Example

Score all columns and compare against a known expected score vector:

import bitbully.bitbully_core as bbc

solver = bbc.BitBullyCore()
board = bbc.BoardCore()

move_sequence = [3, 4, 1, 1, 0, 2, 2, 2]
assert board.setBoard(move_sequence)

scores = solver.scoreMoves(board)
assert scores == [-3, -3, 1, -4, 3, -2, -2]

Example

Illegal moves are included and use the engine sentinel value (-1000):

import bitbully.bitbully_core as bbc

solver = bbc.BitBullyCore()
board = bbc.BoardCore()
assert board.setBoard([3, 3, 3, 3, 3, 3, 4, 2])

scores = solver.scoreMoves(board)
assert scores == [-2, -2, 2, -1000, -2, 1, -1]

# Column 3 is full here (sentinel score).
assert board.isLegalMove(3) is False
assert scores[3] == -1000

Source code in src/bitbully/bitbully_core.pyi
def scoreMoves(self, board: "BoardCore") -> list[int]:
    """Evaluate all columns (0..6) in the given position.

    Args:
        board (BoardCore): Current position.

    Returns:
        list[int]: A list of length 7 with per-column scores. Illegal moves
            (full columns) are included and use an engine-defined sentinel value.

    Example:
        Score all moves and pick the best:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        assert board.play("334411")

        solver = bbc.BitBullyCore()
        scores = solver.scoreMoves(board)
        assert len(scores) == 7

        best_col = max(range(7), key=scores.__getitem__)
        assert 0 <= best_col < 7
        assert board.isLegalMove(best_col) is True
        ```

    Example:
        Score all columns and compare against a known expected score vector:
        ```python
        import bitbully.bitbully_core as bbc

        solver = bbc.BitBullyCore()
        board = bbc.BoardCore()

        move_sequence = [3, 4, 1, 1, 0, 2, 2, 2]
        assert board.setBoard(move_sequence)

        scores = solver.scoreMoves(board)
        assert scores == [-3, -3, 1, -4, 3, -2, -2]
        ```

    Example:
        Illegal moves are included and use the engine sentinel value (-1000):
        ```python
        import bitbully.bitbully_core as bbc

        solver = bbc.BitBullyCore()
        board = bbc.BoardCore()
        assert board.setBoard([3, 3, 3, 3, 3, 3, 4, 2])

        scores = solver.scoreMoves(board)
        assert scores == [-2, -2, 2, -1000, -2, 1, -1]

        # Column 3 is full here (sentinel score).
        assert board.isLegalMove(3) is False
        assert scores[3] == -1000
        ```
    """

BoardCore

Low-level Connect-4 board representation (bitboard-based).

This class is optimized for speed and is the main input type for the solver. It supports playing moves, mirroring, hashing/UIDs, win checks, and move generation.

Notes
  • Column indices are 0..6 (left to right).
  • The side to move is part of the position state.
  • Many methods correspond 1:1 to C++ engine functions.
Example

Create a board, play a sequence, and print:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play("33333111")
print(board.toString())
Expected output:
_  _  _  _  _  _  _
_  _  _  X  _  _  _
_  _  _  O  _  _  _
_  O  _  X  _  _  _
_  X  _  O  _  _  _
_  O  _  X  _  _  _

Methods:

Name Description
__eq__

Compare two boards for exact position equality.

__ne__

Compare two boards for inequality.

allPositions

Generate all reachable positions from the current board up to a ply limit.

copy

Create a deep copy of the board.

countTokens

Return the number of tokens currently on the board.

doubleThreat

Compute double-threat information (engine-specific).

findThreats

Compute threat information (engine-specific).

generateNonLosingMoves

Return a bitmask of non-losing legal moves (engine definition).

getColumnHeight

Return the number of tokens in the given column.

hasWin

Check whether the player who made the last move has a connect-four.

hash

Return a hash of the current position.

isLegalMove

Check whether playing in column is legal (in-range and not full).

isValid

Check whether a 7x6 column-major token grid is valid.

legalMoves

Return legal moves as a list of column indices.

legalMovesMask

Return the legal moves as a bitmask.

mirror

Return the horizontally mirrored position.

movesLeft

Return the number of empty cells remaining.

playMoveOnCopy

Return a new board with mv applied, leaving the original unchanged.

popCountBoard

Return the number of occupied cells (popcount of the token bitboard).

randomBoard

Generate a random reachable position by playing random moves.

toArray

Return the current position as a 7x6 column-major token grid.

toHuffman

Encode the current position into the engine's Huffman representation.

toString

Return a human-readable ASCII rendering of the board.

uid

Return a deterministic unique identifier for the current position.

Attributes:

Name Type Description
__hash__ None

__hash__ class-attribute

__hash__: None = None

__eq__

__eq__(arg0: BoardCore) -> bool

Compare two boards for exact position equality.

Parameters:

Name Type Description Default

arg0

BoardCore

Other board.

required

Returns:

Name Type Description
bool bool

True if both boards represent the same position.

Example

Compare two independently built boards:

import bitbully.bitbully_core as bbc

b1 = bbc.BoardCore()
b2 = bbc.BoardCore()
assert b1.play("3344")
assert b2.play("3344")

assert b1 == b2

Source code in src/bitbully/bitbully_core.pyi
def __eq__(self, arg0: "BoardCore") -> bool:
    """Compare two boards for exact position equality.

    Args:
        arg0 (BoardCore): Other board.

    Returns:
        bool: ``True`` if both boards represent the same position.

    Example:
        Compare two independently built boards:
        ```python
        import bitbully.bitbully_core as bbc

        b1 = bbc.BoardCore()
        b2 = bbc.BoardCore()
        assert b1.play("3344")
        assert b2.play("3344")

        assert b1 == b2
        ```
    """

__ne__

__ne__(arg0: BoardCore) -> bool

Compare two boards for inequality.

Parameters:

Name Type Description Default

arg0

BoardCore

Other board.

required

Returns:

Name Type Description
bool bool

True if the boards differ, otherwise False.

Example

Build two different positions and compare:

import bitbully.bitbully_core as bbc

b1 = bbc.BoardCore()
b2 = bbc.BoardCore()
assert b1.play("3")
assert b2.play("4")

assert b1 != b2

Source code in src/bitbully/bitbully_core.pyi
def __ne__(self, arg0: "BoardCore") -> bool:
    """Compare two boards for inequality.

    Args:
        arg0 (BoardCore): Other board.

    Returns:
        bool: ``True`` if the boards differ, otherwise ``False``.

    Example:
        Build two different positions and compare:
        ```python
        import bitbully.bitbully_core as bbc

        b1 = bbc.BoardCore()
        b2 = bbc.BoardCore()
        assert b1.play("3")
        assert b2.play("4")

        assert b1 != b2
        ```
    """

allPositions

allPositions(upToNPly: int, exactlyN: bool) -> list[BoardCore]

Generate all reachable positions from the current board up to a ply limit.

Parameters:

Name Type Description Default

upToNPly

int

Maximum total token count for generated positions.

required

exactlyN

bool

If True, return only positions with exactly upToNPly tokens. If False, include all positions from the current ply up to upToNPly.

required

Returns:

Type Description
list[BoardCore]

list[BoardCore]: List of generated positions.

Example

Enumerate all positions with exactly 2 tokens from the empty board:

import bitbully.bitbully_core as bbc

root = bbc.BoardCore()
positions = root.allPositions(upToNPly=2, exactlyN=True)

assert all(p.countTokens() == 2 for p in positions)
print("Count:", len(positions))
Expected output:
    Count: 49

Source code in src/bitbully/bitbully_core.pyi
def allPositions(self, upToNPly: int, exactlyN: bool) -> list["BoardCore"]:
    """Generate all reachable positions from the current board up to a ply limit.

    Args:
        upToNPly (int): Maximum total token count for generated positions.
        exactlyN (bool): If ``True``, return only positions with exactly
            ``upToNPly`` tokens. If ``False``, include all positions from the
            current ply up to ``upToNPly``.

    Returns:
        list[BoardCore]: List of generated positions.

    Example:
        Enumerate all positions with exactly 2 tokens from the empty board:
        ```python
        import bitbully.bitbully_core as bbc

        root = bbc.BoardCore()
        positions = root.allPositions(upToNPly=2, exactlyN=True)

        assert all(p.countTokens() == 2 for p in positions)
        print("Count:", len(positions))
        ```
        Expected output:
        ```text
            Count: 49
        ```
    """

copy

copy() -> BoardCore

Create a deep copy of the board.

Returns:

Name Type Description
BoardCore BoardCore

Independent copy of the current position.

Example

Create a board, copy it, and verify that both represent the same position:

import bitbully.bitbully_core as bbc

# Create a board from a compact move string.
board = bbc.BoardCore()
assert board.play("33333111")

# Create an independent copy of the current position.
board_copy = board.copy()

# Both boards represent the same position and are considered equal.
assert board == board_copy
assert board.uid() == board_copy.uid()
assert board.toString() == board_copy.toString()

# Display the board state.
print(board.toString())
Expected output:
  _  _  _  _  _  _  _
  _  _  _  X  _  _  _
  _  _  _  O  _  _  _
  _  O  _  X  _  _  _
  _  X  _  O  _  _  _
  _  O  _  X  _  _  _

Source code in src/bitbully/bitbully_core.pyi
def copy(self) -> "BoardCore":
    """Create a deep copy of the board.

    Returns:
        BoardCore: Independent copy of the current position.

    Example:
        Create a board, copy it, and verify that both represent the same position:
        ```python
        import bitbully.bitbully_core as bbc

        # Create a board from a compact move string.
        board = bbc.BoardCore()
        assert board.play("33333111")

        # Create an independent copy of the current position.
        board_copy = board.copy()

        # Both boards represent the same position and are considered equal.
        assert board == board_copy
        assert board.uid() == board_copy.uid()
        assert board.toString() == board_copy.toString()

        # Display the board state.
        print(board.toString())
        ```
        Expected output:
        ```text
          _  _  _  _  _  _  _
          _  _  _  X  _  _  _
          _  _  _  O  _  _  _
          _  O  _  X  _  _  _
          _  X  _  O  _  _  _
          _  O  _  X  _  _  _
        ```
    """

countTokens

countTokens() -> int

Return the number of tokens currently on the board.

Returns:

Name Type Description
int int

Token count (0-42).

Example

Count tokens after playing a move string:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play("3344")
assert board.countTokens() == 4

Source code in src/bitbully/bitbully_core.pyi
def countTokens(self) -> int:
    """Return the number of tokens currently on the board.

    Returns:
        int: Token count (0-42).

    Example:
        Count tokens after playing a move string:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        assert board.play("3344")
        assert board.countTokens() == 4
        ```
    """

doubleThreat

doubleThreat(moves: int) -> int

Compute double-threat information (engine-specific).

Parameters:

Name Type Description Default

moves

int

Move mask / move set parameter as expected by the engine. A typical input is the result of legalMovesMask().

required

Returns:

Name Type Description
int int

Engine-defined bitmask/encoding of detected double threats.

Source code in src/bitbully/bitbully_core.pyi
def doubleThreat(self, moves: int) -> int:
    """Compute double-threat information (engine-specific).

    Args:
        moves (int): Move mask / move set parameter as expected by the engine.
            A typical input is the result of ``legalMovesMask()``.

    Returns:
        int: Engine-defined bitmask/encoding of detected double threats.
    """

findThreats

findThreats(moves: int) -> int

Compute threat information (engine-specific).

Parameters:

Name Type Description Default

moves

int

Move mask / move set parameter as expected by the engine. A typical input is the result of legalMovesMask().

required

Returns:

Name Type Description
int int

Engine-defined bitmask/encoding of detected threats.

Source code in src/bitbully/bitbully_core.pyi
def findThreats(self, moves: int) -> int:
    """Compute threat information (engine-specific).

    Args:
        moves (int): Move mask / move set parameter as expected by the engine.
            A typical input is the result of ``legalMovesMask()``.

    Returns:
        int: Engine-defined bitmask/encoding of detected threats.
    """

generateNonLosingMoves

generateNonLosingMoves() -> int

Return a bitmask of non-losing legal moves (engine definition).

Returns:

Name Type Description
int int

Bitmask encoding non-losing moves for the side to move.

Example

Generate non-losing moves and compare with all legal moves:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play("334411")

legal = board.legalMovesMask()
non_losing = board.generateNonLosingMoves()

# Non-losing is a subset of legal (bitwise).
assert (non_losing & legal) == non_losing

Source code in src/bitbully/bitbully_core.pyi
def generateNonLosingMoves(self) -> int:
    """Return a bitmask of non-losing legal moves (engine definition).

    Returns:
        int: Bitmask encoding non-losing moves for the side to move.

    Example:
        Generate non-losing moves and compare with all legal moves:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        assert board.play("334411")

        legal = board.legalMovesMask()
        non_losing = board.generateNonLosingMoves()

        # Non-losing is a subset of legal (bitwise).
        assert (non_losing & legal) == non_losing
        ```
    """

getColumnHeight

getColumnHeight(column: int) -> int

Return the number of tokens in the given column.

Parameters:

Name Type Description Default

column

int

Column index (0-6).

required

Returns:

Name Type Description
int int

Number of tokens in the column (0-6).

Example

Check column heights after some moves:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play("3332211" + 6 * "5")

assert board.getColumnHeight(0) == 0
assert board.getColumnHeight(1) == 2
assert board.getColumnHeight(2) == 2
assert board.getColumnHeight(3) == 3
assert board.getColumnHeight(4) == 0
assert board.getColumnHeight(5) == 6
assert board.getColumnHeight(6) == 0

Source code in src/bitbully/bitbully_core.pyi
def getColumnHeight(self, column: int) -> int:
    """Return the number of tokens in the given column.

    Args:
        column (int): Column index (0-6).

    Returns:
        int: Number of tokens in the column (0-6).

    Example:
        Check column heights after some moves:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        assert board.play("3332211" + 6 * "5")

        assert board.getColumnHeight(0) == 0
        assert board.getColumnHeight(1) == 2
        assert board.getColumnHeight(2) == 2
        assert board.getColumnHeight(3) == 3
        assert board.getColumnHeight(4) == 0
        assert board.getColumnHeight(5) == 6
        assert board.getColumnHeight(6) == 0
        ```
    """

hasWin

hasWin() -> bool

Check whether the player who made the last move has a connect-four.

Returns:

Name Type Description
bool bool

True if the previous player has a winning 4-in-a-row.

Example

Play a winning sequence and check that the last mover has a win:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play([3, 2, 3, 2, 3, 2, 3])
assert board.hasWin() is True

Source code in src/bitbully/bitbully_core.pyi
def hasWin(self) -> bool:
    """Check whether the player who made the last move has a connect-four.

    Returns:
        bool: ``True`` if the previous player has a winning 4-in-a-row.

    Example:
        Play a winning sequence and check that the last mover has a win:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        assert board.play([3, 2, 3, 2, 3, 2, 3])
        assert board.hasWin() is True
        ```
    """

hash

hash() -> int

Return a hash of the current position.

Returns:

Name Type Description
int int

Hash value suitable for hash tables / transposition tables.

Example

Hashes match for identical positions and differ after divergence:

import bitbully.bitbully_core as bbc

b1 = bbc.BoardCore()
b2 = bbc.BoardCore()
assert b1.play("334411")
assert b2.play("334411")
assert b1.hash() == b2.hash()

assert b1.play(1)
assert b1.hash() != b2.hash()

Source code in src/bitbully/bitbully_core.pyi
def hash(self) -> int:
    """Return a hash of the current position.

    Returns:
        int: Hash value suitable for hash tables / transposition tables.

    Example:
        Hashes match for identical positions and differ after divergence:
        ```python
        import bitbully.bitbully_core as bbc

        b1 = bbc.BoardCore()
        b2 = bbc.BoardCore()
        assert b1.play("334411")
        assert b2.play("334411")
        assert b1.hash() == b2.hash()

        assert b1.play(1)
        assert b1.hash() != b2.hash()
        ```
    """

isLegalMove

isLegalMove(column: int) -> bool

Check whether playing in column is legal (in-range and not full).

Parameters:

Name Type Description Default

column

int

Column index (0-6).

required

Returns:

Name Type Description
bool bool

True if the move is legal, otherwise False.

Example

Check legality before playing:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
if board.isLegalMove(3):
    assert board.play(3)

Source code in src/bitbully/bitbully_core.pyi
def isLegalMove(self, column: int) -> bool:
    """Check whether playing in ``column`` is legal (in-range and not full).

    Args:
        column (int): Column index (0-6).

    Returns:
        bool: ``True`` if the move is legal, otherwise ``False``.

    Example:
        Check legality before playing:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        if board.isLegalMove(3):
            assert board.play(3)
        ```
    """

isValid staticmethod

isValid(board: list[list[int]]) -> bool

Check whether a 7x6 column-major token grid is valid.

Parameters:

Name Type Description Default

board

list[list[int]]

Column-major 7x6 grid (board[col][row]) with values typically in {0, 1, 2} (empty/yellow/red).

required

Returns:

Name Type Description
bool bool

True if the array has the right shape and encodes a legal position according to engine rules.

Example

Validate a board grid before setting it:

import bitbully.bitbully_core as bbc

grid = [[0] * 6 for _ in range(7)]
grid[3][0] = int(bbc.Player.P_YELLOW)
assert bbc.BoardCore.isValid(grid) is True

grid[3][0] = 3  # invalid token
assert bbc.BoardCore.isValid(grid) is False

Source code in src/bitbully/bitbully_core.pyi
@staticmethod
def isValid(
    board: typing.Annotated[
        list[typing.Annotated[list[int], pybind11_stubgen.typing_ext.FixedSize(6)]],
        pybind11_stubgen.typing_ext.FixedSize(7),
    ],
) -> bool:
    """Check whether a 7x6 column-major token grid is valid.

    Args:
        board (list[list[int]]): Column-major 7x6 grid (``board[col][row]``)
            with values typically in ``{0, 1, 2}`` (empty/yellow/red).

    Returns:
        bool: ``True`` if the array has the right shape and encodes a legal
            position according to engine rules.

    Example:
        Validate a board grid before setting it:
        ```python
        import bitbully.bitbully_core as bbc

        grid = [[0] * 6 for _ in range(7)]
        grid[3][0] = int(bbc.Player.P_YELLOW)
        assert bbc.BoardCore.isValid(grid) is True

        grid[3][0] = 3  # invalid token
        assert bbc.BoardCore.isValid(grid) is False
        ```
    """

legalMoves

legalMoves(nonLosing: bool, orderMoves: bool) -> list[int]

Return legal moves as a list of column indices.

Parameters:

Name Type Description Default

nonLosing

bool

If True, return only moves that do not allow the opponent to win immediately next turn (engine definition).

required

orderMoves

bool

If True, order moves in an engine-defined heuristic order (typically center-first).

required

Returns:

Type Description
list[int]

list[int]: List of legal column indices.

Example

Get ordered legal moves (center-first):

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
cols = board.legalMoves(nonLosing=False, orderMoves=True)

assert all(0 <= c < 7 for c in cols)
print(cols)
Expected output:
[3, 2, 4, 1, 5, 0, 6]

Source code in src/bitbully/bitbully_core.pyi
def legalMoves(self, nonLosing: bool, orderMoves: bool) -> list[int]:
    """Return legal moves as a list of column indices.

    Args:
        nonLosing (bool): If ``True``, return only moves that do not allow the
            opponent to win immediately next turn (engine definition).
        orderMoves (bool): If ``True``, order moves in an engine-defined
            heuristic order (typically center-first).

    Returns:
        list[int]: List of legal column indices.

    Example:
        Get ordered legal moves (center-first):
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        cols = board.legalMoves(nonLosing=False, orderMoves=True)

        assert all(0 <= c < 7 for c in cols)
        print(cols)
        ```
        Expected output:
        ```text
        [3, 2, 4, 1, 5, 0, 6]
        ```
    """

legalMovesMask

legalMovesMask() -> int

Return the legal moves as a bitmask.

Returns:

Name Type Description
int int

Bitmask encoding the set of legal moves (engine bitboard format).

Example

Get the move mask and verify it is non-zero on non-full boards:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
mask = board.legalMovesMask()
assert isinstance(mask, int)
assert mask != 0

Source code in src/bitbully/bitbully_core.pyi
def legalMovesMask(self) -> int:
    """Return the legal moves as a bitmask.

    Returns:
        int: Bitmask encoding the set of legal moves (engine bitboard format).

    Example:
        Get the move mask and verify it is non-zero on non-full boards:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        mask = board.legalMovesMask()
        assert isinstance(mask, int)
        assert mask != 0
        ```
    """

mirror

mirror() -> BoardCore

Return the horizontally mirrored position.

Returns:

Name Type Description
BoardCore BoardCore

Mirrored board (column 0 <-> 6, 1 <-> 5, 2 <-> 4).

Example

Mirror twice returns the original position:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.setBoard([0, 1, 2])  # simple asymmetric position

mirrored = board.mirror()
assert mirrored != board
assert mirrored.countTokens() == board.countTokens()
assert mirrored.mirror() == board

Source code in src/bitbully/bitbully_core.pyi
def mirror(self) -> "BoardCore":
    """Return the horizontally mirrored position.

    Returns:
        BoardCore: Mirrored board (column 0 <-> 6, 1 <-> 5, 2 <-> 4).

    Example:
        Mirror twice returns the original position:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        assert board.setBoard([0, 1, 2])  # simple asymmetric position

        mirrored = board.mirror()
        assert mirrored != board
        assert mirrored.countTokens() == board.countTokens()
        assert mirrored.mirror() == board
        ```
    """

movesLeft

movesLeft() -> int

Return the number of empty cells remaining.

Returns:

Name Type Description
int int

Remaining moves until the board is full (0-42).

Example

Confirm moves left after some moves:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play("3344")
assert board.movesLeft() == 42 - 4

Source code in src/bitbully/bitbully_core.pyi
def movesLeft(self) -> int:
    """Return the number of empty cells remaining.

    Returns:
        int: Remaining moves until the board is full (0-42).

    Example:
        Confirm moves left after some moves:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        assert board.play("3344")
        assert board.movesLeft() == 42 - 4
        ```
    """

playMoveOnCopy

playMoveOnCopy(mv: int) -> BoardCore

Return a new board with mv applied, leaving the original unchanged.

Parameters:

Name Type Description Default

mv

int

Column index (0-6).

required

Returns:

Name Type Description
BoardCore BoardCore

New board after the move.

Example

Try a move without mutating the original board:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
board2 = board.playMoveOnCopy(3)

assert board.countTokens() == 0
assert board2.countTokens() == 1

Source code in src/bitbully/bitbully_core.pyi
def playMoveOnCopy(self, mv: int) -> "BoardCore":
    """Return a new board with ``mv`` applied, leaving the original unchanged.

    Args:
        mv (int): Column index (0-6).

    Returns:
        BoardCore: New board after the move.

    Example:
        Try a move without mutating the original board:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        board2 = board.playMoveOnCopy(3)

        assert board.countTokens() == 0
        assert board2.countTokens() == 1
        ```
    """

popCountBoard

popCountBoard() -> int

Return the number of occupied cells (popcount of the token bitboard).

Returns:

Name Type Description
int int

Number of occupied cells (0-42).

Example

Compare popCountBoard() with countTokens():

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play("334411")
assert board.popCountBoard() == board.countTokens()

Source code in src/bitbully/bitbully_core.pyi
def popCountBoard(self) -> int:
    """Return the number of occupied cells (popcount of the token bitboard).

    Returns:
        int: Number of occupied cells (0-42).

    Example:
        Compare popCountBoard() with countTokens():
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        assert board.play("334411")
        assert board.popCountBoard() == board.countTokens()
        ```
    """

randomBoard staticmethod

Generate a random reachable position by playing random moves.

Parameters:

Name Type Description Default

nPly

int

Number of moves (tokens) to play (0-42).

required

forbidDirectWin

bool

If True, ensure the generated position does not contain an immediate winning move for the side to move.

required

Returns:

Type Description
tuple[BoardCore, list[int]]

tuple[BoardCore, list[int]]: (board, moves) where moves is the move sequence used to generate the board.

Example

Create a random 8-ply position and show the move sequence:

import bitbully.bitbully_core as bbc

board, moves = bbc.BoardCore.randomBoard(nPly=8, forbidDirectWin=True)
assert len(moves) == 8

print("Moves:", moves)
print(board)

Source code in src/bitbully/bitbully_core.pyi
@staticmethod
def randomBoard(nPly: int, forbidDirectWin: bool) -> tuple["BoardCore", list[int]]:
    """Generate a random reachable position by playing random moves.

    Args:
        nPly (int): Number of moves (tokens) to play (0-42).
        forbidDirectWin (bool): If ``True``, ensure the generated position
            does not contain an immediate winning move for the side to move.

    Returns:
        tuple[BoardCore, list[int]]: ``(board, moves)`` where ``moves`` is the
            move sequence used to generate the board.

    Example:
        Create a random 8-ply position and show the move sequence:
        ```python
        import bitbully.bitbully_core as bbc

        board, moves = bbc.BoardCore.randomBoard(nPly=8, forbidDirectWin=True)
        assert len(moves) == 8

        print("Moves:", moves)
        print(board)
        ```
    """

toArray

toArray() -> list[list[int]]

Return the current position as a 7x6 column-major token grid.

Returns:

Type Description
list[list[int]]

list[list[int]]: Column-major grid (7 columns x 6 rows).

Example

Convert to an array and inspect dimensions:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play("3")

arr = board.toArray()
assert len(arr) == 7
assert len(arr[0]) == 6

Source code in src/bitbully/bitbully_core.pyi
def toArray(
    self,
) -> typing.Annotated[
    list[typing.Annotated[list[int], pybind11_stubgen.typing_ext.FixedSize(6)]],
    pybind11_stubgen.typing_ext.FixedSize(7),
]:
    """Return the current position as a 7x6 column-major token grid.

    Returns:
        list[list[int]]: Column-major grid (7 columns x 6 rows).

    Example:
        Convert to an array and inspect dimensions:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        assert board.play("3")

        arr = board.toArray()
        assert len(arr) == 7
        assert len(arr[0]) == 6
        ```
    """

toHuffman

toHuffman() -> int

Encode the current position into the engine's Huffman representation.

Returns:

Name Type Description
int int

Huffman-encoded position key used by the opening books.

Example

Compute a Huffman key (only defined for certain positions):

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()

# Huffman encoding is only defined for some positions in this engine
# (e.g., even number of tokens and <= 12 ply in your C++ code).
assert board.play("33331111")  # 8 tokens
key = board.toHuffman()
print("Huffman key:", key)
Expected output:
    Huffman key: 6133600

Source code in src/bitbully/bitbully_core.pyi
def toHuffman(self) -> int:
    """Encode the current position into the engine's Huffman representation.

    Returns:
        int: Huffman-encoded position key used by the opening books.

    Example:
        Compute a Huffman key (only defined for certain positions):
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()

        # Huffman encoding is only defined for some positions in this engine
        # (e.g., even number of tokens and <= 12 ply in your C++ code).
        assert board.play("33331111")  # 8 tokens
        key = board.toHuffman()
        print("Huffman key:", key)
        ```
        Expected output:
        ```text
            Huffman key: 6133600
        ```
    """

toString

toString() -> str

Return a human-readable ASCII rendering of the board.

Returns:

Name Type Description
str str

Multi-line 6x7 grid representation.

Example

Print a board:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play("33333111")
print(board.toString())
Expected output:
_  _  _  _  _  _  _
_  _  _  X  _  _  _
_  _  _  O  _  _  _
_  O  _  X  _  _  _
_  X  _  O  _  _  _
_  O  _  X  _  _  _

Source code in src/bitbully/bitbully_core.pyi
def toString(self) -> str:
    """Return a human-readable ASCII rendering of the board.

    Returns:
        str: Multi-line 6x7 grid representation.

    Example:
        Print a board:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        assert board.play("33333111")
        print(board.toString())
        ```
        Expected output:
        ```text
        _  _  _  _  _  _  _
        _  _  _  X  _  _  _
        _  _  _  O  _  _  _
        _  O  _  X  _  _  _
        _  X  _  O  _  _  _
        _  O  _  X  _  _  _
        ```
    """

uid

uid() -> int

Return a deterministic unique identifier for the current position.

Returns:

Name Type Description
int int

UID derived from the position (tokens + side to move).

Example

Use uid() as a stable key for caching:

import bitbully.bitbully_core as bbc

board = bbc.BoardCore()
assert board.play("334411")

cache: dict[int, str] = {}
cache[board.uid()] = board.toString()
assert board.uid() in cache

Source code in src/bitbully/bitbully_core.pyi
def uid(self) -> int:
    """Return a deterministic unique identifier for the current position.

    Returns:
        int: UID derived from the position (tokens + side to move).

    Example:
        Use ``uid()`` as a stable key for caching:
        ```python
        import bitbully.bitbully_core as bbc

        board = bbc.BoardCore()
        assert board.play("334411")

        cache: dict[int, str] = {}
        cache[board.uid()] = board.toString()
        assert board.uid() in cache
        ```
    """

OpeningBookCore

Opening book reader and lookup helper.

Opening books map a compact position key (Huffman encoding) to an engine score, optionally including distance-to-win information.

Example

Load a book and check whether a position is contained:

import bitbully.bitbully_core as bbc
import bitbully_databases as bbd
db_path = bbd.BitBullyDatabases.get_database_path("default")

board = bbc.BoardCore()
assert board.play("333331111555")

book = bbc.OpeningBookCore(db_path)  # replace with your file
assert book.isInBook(board)

Methods:

Name Description
convertValue

Convert a stored book value to an engine score for the given board.

getBoardValue

Lookup a board position in the opening book and return its value.

getBook

Return the raw opening book table.

getBookSize

Return the number of entries in the opening book.

getEntry

Return a single raw entry by index.

getNPly

Return the ply depth of the opening book.

init

Reinitialize the opening book with new settings.

isInBook

Check whether a position exists in the opening book.

readBook

Read an opening book file into a raw table.

convertValue

convertValue(value: int, board: BoardCore) -> int

Convert a stored book value to an engine score for the given board.

Parameters:

Name Type Description Default

value

int

Raw value stored in the book table.

required

board

BoardCore

Board used to interpret the value.

required

Returns:

Name Type Description
int int

Converted score in the engine's scoring convention.

Example

Convert a raw entry value using the current board context:

import bitbully.bitbully_core as bbc
import bitbully_databases as bbd
db_path = bbd.BitBullyDatabases.get_database_path("default")

board = bbc.BoardCore()
book = bbc.OpeningBookCore(db_path)  # replace with your file

key, raw_val = book.getEntry(0)
score = book.convertValue(raw_val, board)
print("Converted score:", score)
Expected output:
    Converted score: 9

Source code in src/bitbully/bitbully_core.pyi
def convertValue(self, value: int, board: "BoardCore") -> int:
    """Convert a stored book value to an engine score for the given board.

    Args:
        value (int): Raw value stored in the book table.
        board (BoardCore): Board used to interpret the value.

    Returns:
        int: Converted score in the engine's scoring convention.

    Example:
        Convert a raw entry value using the current board context:
        ```python
        import bitbully.bitbully_core as bbc
        import bitbully_databases as bbd
        db_path = bbd.BitBullyDatabases.get_database_path("default")

        board = bbc.BoardCore()
        book = bbc.OpeningBookCore(db_path)  # replace with your file

        key, raw_val = book.getEntry(0)
        score = book.convertValue(raw_val, board)
        print("Converted score:", score)
        ```
        Expected output:
        ```text
            Converted score: 9
        ```
    """

getBoardValue

getBoardValue(board: BoardCore) -> int

Lookup a board position in the opening book and return its value.

Parameters:

Name Type Description Default

board

BoardCore

Position to query.

required

Returns:

Name Type Description
int int

Book value converted to the engine's scoring convention.

Example

Query a position's book value:

import bitbully.bitbully_core as bbc
import bitbully_databases as bbd
db_path = bbd.BitBullyDatabases.get_database_path("default")

board = bbc.BoardCore()
assert board.play("333331111555")

book = bbc.OpeningBookCore(db_path)  # replace with your file
if book.isInBook(board):
    print("Value:", book.getBoardValue(board))
Expected output:
    Value: 1

Source code in src/bitbully/bitbully_core.pyi
def getBoardValue(self, board: "BoardCore") -> int:
    """Lookup a board position in the opening book and return its value.

    Args:
        board (BoardCore): Position to query.

    Returns:
        int: Book value converted to the engine's scoring convention.

    Example:
        Query a position's book value:
        ```python
        import bitbully.bitbully_core as bbc
        import bitbully_databases as bbd
        db_path = bbd.BitBullyDatabases.get_database_path("default")

        board = bbc.BoardCore()
        assert board.play("333331111555")

        book = bbc.OpeningBookCore(db_path)  # replace with your file
        if book.isInBook(board):
            print("Value:", book.getBoardValue(board))
        ```
        Expected output:
        ```text
            Value: 1
        ```
    """

getBook

getBook() -> list[tuple[int, int]]

Return the raw opening book table.

Returns:

Type Description
list[tuple[int, int]]

list[tuple[int, int]]: List of (key, value) entries.

Example

Access the raw table and inspect the first entry:

import bitbully.bitbully_core as bbc
import bitbully_databases as bbd
db_path = bbd.BitBullyDatabases.get_database_path("default")

book = bbc.OpeningBookCore(db_path)  # replace with your file
raw = book.getBook()

key0, val0 = raw[0]
print("First entry:", key0, val0)
Expected output:
    First entry: -2124988676 75

Source code in src/bitbully/bitbully_core.pyi
def getBook(self) -> list[tuple[int, int]]:
    """Return the raw opening book table.

    Returns:
        list[tuple[int, int]]: List of ``(key, value)`` entries.

    Example:
        Access the raw table and inspect the first entry:
        ```python
        import bitbully.bitbully_core as bbc
        import bitbully_databases as bbd
        db_path = bbd.BitBullyDatabases.get_database_path("default")

        book = bbc.OpeningBookCore(db_path)  # replace with your file
        raw = book.getBook()

        key0, val0 = raw[0]
        print("First entry:", key0, val0)
        ```
        Expected output:
        ```text
            First entry: -2124988676 75
        ```
    """

getBookSize

getBookSize() -> int

Return the number of entries in the opening book.

Returns:

Name Type Description
int int

Number of stored positions.

Example

Print the number of stored positions:

import bitbully.bitbully_core as bbc
import bitbully_databases as bbd
db_path = bbd.BitBullyDatabases.get_database_path("default")

book = bbc.OpeningBookCore(db_path)  # replace with your file
print(book.getBookSize())
Expected output:
    4200899

Source code in src/bitbully/bitbully_core.pyi
def getBookSize(self) -> int:
    """Return the number of entries in the opening book.

    Returns:
        int: Number of stored positions.

    Example:
        Print the number of stored positions:
        ```python
        import bitbully.bitbully_core as bbc
        import bitbully_databases as bbd
        db_path = bbd.BitBullyDatabases.get_database_path("default")

        book = bbc.OpeningBookCore(db_path)  # replace with your file
        print(book.getBookSize())
        ```
        Expected output:
        ```text
            4200899
        ```
    """

getEntry

getEntry(entryIdx: int) -> tuple[int, int]

Return a single raw entry by index.

Parameters:

Name Type Description Default

entryIdx

int

Entry index (0-based).

required

Returns:

Type Description
tuple[int, int]

tuple[int, int]: The (key, value) pair at entryIdx.

Example

Read a single entry:

import bitbully.bitbully_core as bbc
import bitbully_databases as bbd
db_path = bbd.BitBullyDatabases.get_database_path("default")

book = bbc.OpeningBookCore(db_path)  # replace with your file
key, val = book.getEntry(0)

print("Entry 0:", key, val)
Expected output:
    Entry 0: -2124988676 75

Source code in src/bitbully/bitbully_core.pyi
def getEntry(self, entryIdx: int) -> tuple[int, int]:
    """Return a single raw entry by index.

    Args:
        entryIdx (int): Entry index (0-based).

    Returns:
        tuple[int, int]: The ``(key, value)`` pair at ``entryIdx``.

    Example:
        Read a single entry:
        ```python
        import bitbully.bitbully_core as bbc
        import bitbully_databases as bbd
        db_path = bbd.BitBullyDatabases.get_database_path("default")

        book = bbc.OpeningBookCore(db_path)  # replace with your file
        key, val = book.getEntry(0)

        print("Entry 0:", key, val)
        ```
        Expected output:
        ```text
            Entry 0: -2124988676 75
        ```
    """

getNPly

getNPly() -> int

Return the ply depth of the opening book.

Returns:

Name Type Description
int int

Ply depth (e.g., 8 or 12).

Example

Print the book depth:

import bitbully.bitbully_core as bbc
import bitbully_databases as bbd
db_path = bbd.BitBullyDatabases.get_database_path("default")

book = bbc.OpeningBookCore(db_path)  # replace with your file
print("NPly:", book.getNPly())
Expected output:
    NPly: 12

Source code in src/bitbully/bitbully_core.pyi
def getNPly(self) -> int:
    """Return the ply depth of the opening book.

    Returns:
        int: Ply depth (e.g., 8 or 12).

    Example:
        Print the book depth:
        ```python
        import bitbully.bitbully_core as bbc
        import bitbully_databases as bbd
        db_path = bbd.BitBullyDatabases.get_database_path("default")

        book = bbc.OpeningBookCore(db_path)  # replace with your file
        print("NPly:", book.getNPly())
        ```
        Expected output:
        ```text
            NPly: 12
        ```
    """

init

Reinitialize the opening book with new settings.

Parameters:

Name Type Description Default

bookPath

PathLike

Path to the book file.

required

is_8ply

bool

Whether this is an 8-ply book.

required

with_distances

bool

Whether values include distance-to-win information.

required
Example

Reinitialize an existing instance:

import bitbully.bitbully_core as bbc
import bitbully_databases as bbd
db_path = bbd.BitBullyDatabases.get_database_path("12-ply-dist")

book = bbc.OpeningBookCore(db_path)  # replace with your file
book.init(db_path, is_8ply=False, with_distances=True)

Source code in src/bitbully/bitbully_core.pyi
def init(self, bookPath: os.PathLike, is_8ply: bool, with_distances: bool) -> None:
    """Reinitialize the opening book with new settings.

    Args:
        bookPath (os.PathLike): Path to the book file.
        is_8ply (bool): Whether this is an 8-ply book.
        with_distances (bool): Whether values include distance-to-win information.

    Example:
        Reinitialize an existing instance:
        ```python
        import bitbully.bitbully_core as bbc
        import bitbully_databases as bbd
        db_path = bbd.BitBullyDatabases.get_database_path("12-ply-dist")

        book = bbc.OpeningBookCore(db_path)  # replace with your file
        book.init(db_path, is_8ply=False, with_distances=True)
        ```
    """

isInBook

isInBook(board: BoardCore) -> bool

Check whether a position exists in the opening book.

Parameters:

Name Type Description Default

board

BoardCore

Position to check.

required

Returns:

Name Type Description
bool bool

True if the position (or its mirrored canonical form) is present in the book, otherwise False.

Example

Check membership before lookup:

import bitbully.bitbully_core as bbc
import bitbully_databases as bbd

db_path = bbd.BitBullyDatabases.get_database_path("default")

board = bbc.BoardCore()
assert board.play("334411")

book = bbc.OpeningBookCore(db_path)

in_book = book.isInBook(board)
if in_book:
    _ = book.getBoardValue(board)

assert isinstance(in_book, bool)

Source code in src/bitbully/bitbully_core.pyi
def isInBook(self, board: "BoardCore") -> bool:
    """Check whether a position exists in the opening book.

    Args:
        board (BoardCore): Position to check.

    Returns:
        bool: ``True`` if the position (or its mirrored canonical form) is present in the book, otherwise ``False``.

    Example:
        Check membership before lookup:
        ```python
        import bitbully.bitbully_core as bbc
        import bitbully_databases as bbd

        db_path = bbd.BitBullyDatabases.get_database_path("default")

        board = bbc.BoardCore()
        assert board.play("334411")

        book = bbc.OpeningBookCore(db_path)

        in_book = book.isInBook(board)
        if in_book:
            _ = book.getBoardValue(board)

        assert isinstance(in_book, bool)
        ```

    """

readBook staticmethod

readBook(filename: PathLike, with_distances: bool = True, is_8ply: bool = False) -> list[tuple[int, int]]

Read an opening book file into a raw table.

Parameters:

Name Type Description Default

filename

PathLike

Path to the book file.

required

with_distances

bool

If True, interpret values as including distance-to-win information (where supported).

True

is_8ply

bool

If True, interpret the file as an 8-ply book.

False

Returns:

Type Description
list[tuple[int, int]]

list[tuple[int, int]]: List of (key, value) entries.

Example

Read the raw book table:

import bitbully.bitbully_core as bbc
import bitbully_databases as bbd
db_path = bbd.BitBullyDatabases.get_database_path("default")

table = bbc.OpeningBookCore.readBook(
    db_path,
    with_distances=True,
    is_8ply=False,
)
print("Entries:", len(table))
Expected output:
    Entries: 4200899

Source code in src/bitbully/bitbully_core.pyi
@staticmethod
def readBook(
    filename: os.PathLike,
    with_distances: bool = True,
    is_8ply: bool = False,
) -> list[tuple[int, int]]:
    """Read an opening book file into a raw table.

    Args:
        filename (os.PathLike): Path to the book file.
        with_distances (bool): If ``True``, interpret values as including
            distance-to-win information (where supported).
        is_8ply (bool): If ``True``, interpret the file as an 8-ply book.

    Returns:
        list[tuple[int, int]]: List of ``(key, value)`` entries.

    Example:
        Read the raw book table:
        ```python
        import bitbully.bitbully_core as bbc
        import bitbully_databases as bbd
        db_path = bbd.BitBullyDatabases.get_database_path("default")

        table = bbc.OpeningBookCore.readBook(
            db_path,
            with_distances=True,
            is_8ply=False,
        )
        print("Entries:", len(table))
        ```
        Expected output:
        ```text
            Entries: 4200899
        ```
    """

Player


              flowchart TD
              src.bitbully.bitbully_core.Player[Player]

              

              click src.bitbully.bitbully_core.Player href "" "src.bitbully.bitbully_core.Player"
            

Player identifiers used by the engine.

Example

Inspect numeric values used by the engine:

import bitbully.bitbully_core as bbc

assert int(bbc.Player.P_EMPTY) == 0
assert int(bbc.Player.P_YELLOW) == 1
assert int(bbc.Player.P_RED) == 2

Attributes:

Name Type Description
P_EMPTY int

Empty cell marker (no token).

P_RED int

Player 2 / Red token.

P_YELLOW int

Player 1 / Yellow token.

P_EMPTY instance-attribute

P_EMPTY: int

Empty cell marker (no token).

P_RED instance-attribute

P_RED: int

Player 2 / Red token.

P_YELLOW instance-attribute

P_YELLOW: int

Player 1 / Yellow token.