BitBully 0.0.71
Loading...
Searching...
No Matches
bitbully.solver.BitBully Class Reference

Public Member Functions

None __init__ (self, OpeningBookName|None opening_book="default")
str __repr__ (self)
bool is_book_loaded (self)
None reset_transposition_table (self)
int get_node_counter (self)
None reset_node_counter (self)
int score_move (self, Board board, int column, int first_guess=0)
dict[int, int] score_all_moves (self, Board board)
int best_move (self, Board board, *, TieBreakStrategy tie_break="center", random.Random|None rng=None)
int negamax (self, Board board, int alpha=-1000, int beta=1000, int depth=0)
int null_window (self, Board board)
int mtdf (self, Board board, int first_guess=0)
None load_book (self, OpeningBookName|os.PathLike[str]|str book)
None reset_book (self)
tuple[OpeningBookName,...] available_opening_books (cls)

Public Attributes

OpeningBookName|None opening_book_type = opening_book

Protected Attributes

 _core = bitbully_core.BitBullyCore()

Detailed Description

A Connect Four AI agent with optional opening book support.

Todo:
- We have to describe the scoring scheme (range of values and their meaning).

This class is a high-level Python wrapper around
[`bitbully_core.BitBullyCore`][src.bitbully.bitbully_core.BitBullyCore].
It integrates the packaged *BitBully Databases* opening books and
operates on [`bitbully.Board`][src.bitbully.board.Board] objects.

Notes:
    - If an opening book is enabled, it is used automatically for
      early-game positions.
    - For deeper positions or positions outside the database horizon,
      the engine falls back to search-based evaluation.

Example:
    ```python
    from bitbully import BitBully, Board

    agent = BitBully()
    board, _ = Board.random_board(n_ply=14, forbid_direct_win=True)
    print(board)

    # All three search methods should agree on the score
    score_mtdf = agent.mtdf(board)
    score_negamax = agent.negamax(board)
    score_null_window = agent.null_window(board)
    assert score_negamax == score_null_window == score_mtdf
    ```

Example:
    ```python
    from bitbully import BitBully, Board

    board = Board()  # empty board
    agent = BitBully()
    scores = agent.score_all_moves(board)  # get scores for all moves
    assert len(scores) == 7  # there are 7 columns
    assert scores == {3: 1, 2: 0, 4: 0, 1: -1, 5: -1, 0: -2, 6: -2}  # center column is best
    print(scores)
    ```

    Expected Output:
    ```
    {3: 1, 2: 0, 4: 0, 1: -1, 5: -1, 0: -2, 6: -2}
    ```

Definition at line 42 of file solver.py.

Constructor & Destructor Documentation

◆ __init__()

None bitbully.solver.BitBully.__init__ ( self,
OpeningBookName | None opening_book = "default" )
Initialize the BitBully agent.

Args:
    opening_book (OpeningBookName | None):
        Which opening book to load.

        - ``"default"``: Alias for ``"12-ply-dist"``.
        - ``"8-ply"``: 8-ply book with win/loss values.
        - ``"12-ply"``: 12-ply book with win/loss values.
        - ``"12-ply-dist"``: 12-ply book with win/loss *and distance* values.
        - ``None``: Disable opening-book usage entirely.

TODO: Example for initialization with different books.

Definition at line 94 of file solver.py.

Member Function Documentation

◆ __repr__()

str bitbully.solver.BitBully.__repr__ ( self)
Return a concise string representation of the BitBully agent.

Definition at line 121 of file solver.py.

◆ available_opening_books()

tuple[OpeningBookName, ...] bitbully.solver.BitBully.available_opening_books ( cls)
Return the available opening book identifiers.

Returns:
    tuple[OpeningBookName, ...]:
        All supported opening book names, including ``"default"``.

Example:
    ```python
    from bitbully import BitBully

    books = BitBully.available_opening_books()
    print(books)  # ('default', '8-ply', '12-ply', '12-ply-dist')
    ```

Definition at line 504 of file solver.py.

◆ best_move()

int bitbully.solver.BitBully.best_move ( self,
Board board,
* ,
TieBreakStrategy tie_break = "center",
random.Random | None rng = None )
Return the best legal move (column index) for the current player.

All legal moves are scored using :meth:`score_all_moves`. The move(s)
with the highest score are considered best, and ties are resolved
according to ``tie_break``.

Tie-breaking strategies:
    - ``"center"`` (default):
        Prefer the move closest to the center column (3). If still tied,
        choose the smaller column index.
    - ``"leftmost"``:
        Choose the smallest column index among tied moves.
    - ``"random"``:
        Choose uniformly at random among tied moves. An optional
        ``rng`` can be provided for reproducibility.

Args:
    board (Board): The current board state.
    tie_break (TieBreakStrategy):
        Strategy used to resolve ties between equally scoring moves.
    rng (random.Random | None):
        Random number generator used when ``tie_break="random"``.
        If ``None``, the global RNG is used.

Returns:
    int: The selected column index (0-6).

Raises:
    ValueError: If there are no legal moves (board is full) or
        if an unknown tie-breaking strategy is specified.

Example:
    ```python
    from bitbully import BitBully, Board
    import random

    agent = BitBully()
    board = Board()
    best_col = agent.best_move(board)
    assert best_col == 3  # Center column is best on an empty board
    ```

Example:
    ```python
    from bitbully import BitBully, Board
    import random

    agent = BitBully()
    board = Board("341")  # some arbitrary position
    print(board)
    assert agent.best_move(board, tie_break="center") == 3  # Several moves are tied; center is preferred
    assert agent.best_move(board, tie_break="leftmost") == 1  # Leftmost among tied moves
    assert agent.best_move(board, tie_break="random") in {1, 3, 4}  # Random among tied moves

    rng = random.Random(42)  # use own random number generator
    assert agent.best_move(board, tie_break="random", rng=rng) in {1, 3, 4}
    ```
    Expected Output:
    ```
    _  _  _  _  _  _  _
    _  _  _  _  _  _  _
    _  _  _  _  _  _  _
    _  _  _  _  _  _  _
    _  _  _  _  _  _  _
    _  X  _  X  O  _  _
    ```

Definition at line 250 of file solver.py.

◆ get_node_counter()

int bitbully.solver.BitBully.get_node_counter ( self)
Return the number of nodes visited since the last reset.

Returns:
    int: Number of visited nodes.

Example:
    ```python
    from bitbully import BitBully, Board

    agent = BitBully()
    board = Board()
    _ = agent.score_all_moves(board)
    print(f"Nodes visited: {agent.get_node_counter()}")

    # Note that has to be reset manually:
    agent.reset_node_counter()
    assert agent.get_node_counter() == 0
    ```

Definition at line 149 of file solver.py.

◆ is_book_loaded()

bool bitbully.solver.BitBully.is_book_loaded ( self)
Check whether an opening book is loaded.

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

Example:
    ```python
    from bitbully import BitBully

    agent = BitBully()  # per default, the 12-ply-dist book is loaded
    assert agent.is_book_loaded() is True

    # Unload the book
    agent.reset_book()
    assert agent.is_book_loaded() is False
    ```

Definition at line 125 of file solver.py.

◆ load_book()

None bitbully.solver.BitBully.load_book ( self,
OpeningBookName | os.PathLike[str] | str book )
Load an opening book from a file path.

This is a thin wrapper around
[`bitbully_core.BitBullyCore.loadBook`][src.bitbully.bitbully_core.BitBullyCore.loadBook].

Args:
    book (OpeningBookName | os.PathLike[str] | str):
        Name/Identifier (see [`available_opening_books`][src.bitbully.solver.BitBully.available_opening_books])
        or path of the opening book to load.

Raises:
    ValueError:
        If the book identifier/path is invalid or if loading the book fails.

Example:
    ```python
    from bitbully import BitBully
    from pathlib import Path

    which_book = BitBully.available_opening_books()[0]  # e.g., "default"

    agent = BitBully(opening_book=None)  # start without book
    assert agent.is_book_loaded() is False
    agent.load_book(which_book)  # load "default" book
    assert agent.is_book_loaded() is True
    ```

Example:
    ```python
    from bitbully import BitBully
    from pathlib import Path
    import bitbully_databases as bbd

    which_book = BitBully.available_opening_books()[2]  # e.g., "12-ply"
    db_path = bbd.BitBullyDatabases.get_database_path(which_book)

    agent = BitBully(opening_book=None)  # start without book
    assert agent.is_book_loaded() is False
    agent.load_book(db_path)
    assert agent.is_book_loaded() is True
    ```

Definition at line 422 of file solver.py.

◆ mtdf()

int bitbully.solver.BitBully.mtdf ( self,
Board board,
int first_guess = 0 )
Evaluate a position using the MTD(f) algorithm.

Args:
    board (Board): The board position to evaluate.
    first_guess (int): Initial guess for the score (often 0).

Returns:
    int: The evaluation score.

Example:
    ```python
    from bitbully import BitBully, Board

    agent = BitBully()
    board = Board()
    score = agent.mtdf(board)
    assert score == 1  # Expected score for an empty board
    ```

Definition at line 400 of file solver.py.

◆ negamax()

int bitbully.solver.BitBully.negamax ( self,
Board board,
int alpha = -1000,
int beta = 1000,
int depth = 0 )
Evaluate a position using negamax search.

Args:
    board (Board): The board position to evaluate.
    alpha (int): Alpha bound.
    beta (int): Beta bound.
    depth (int): Search depth in plies.

Returns:
    int: The evaluation score returned by the engine.

Example:
    ```python
    from bitbully import BitBully, Board

    agent = BitBully()
    board = Board()
    score = agent.negamax(board)
    assert score == 1  # Expected score for an empty board
    ```

Definition at line 348 of file solver.py.

◆ null_window()

int bitbully.solver.BitBully.null_window ( self,
Board board )
Evaluate a position using a null-window search.

Args:
    board (Board): The board position to evaluate.

Returns:
    int: The evaluation score.

Example:
    ```python
    from bitbully import BitBully, Board

    agent = BitBully()
    board = Board()
    score = agent.null_window(board)
    assert score == 1  # Expected score for an empty board
    ```

Definition at line 379 of file solver.py.

◆ reset_book()

None bitbully.solver.BitBully.reset_book ( self)
Unload the currently loaded opening book (if any).

This resets the engine to *search-only* mode until another
opening book is loaded.

Example:
    ```python
    from bitbully import BitBully

    agent = BitBully()  # per default, the 12-ply-dist book is loaded
    assert agent.is_book_loaded() is True
    agent.reset_book()
    assert agent.is_book_loaded() is False
    ```

Definition at line 484 of file solver.py.

◆ reset_node_counter()

None bitbully.solver.BitBully.reset_node_counter ( self)
Reset the internal node counter.

See Also: [`get_node_counter`][src.bitbully.solver.BitBully.get_node_counter] for usage.

Definition at line 171 of file solver.py.

◆ reset_transposition_table()

None bitbully.solver.BitBully.reset_transposition_table ( self)
Clear the internal transposition table.

Definition at line 145 of file solver.py.

◆ score_all_moves()

dict[int, int] bitbully.solver.BitBully.score_all_moves ( self,
Board board )
Score all legal moves for the given board state.

Args:
    board (Board): The current board state.

Returns:
    dict[int, int]:
        A dictionary of up to 7 column-value pairs, one per reachable column (0-6).
        Higher values generally indicate better moves for the player to move. If a
        column is full, it will not be listed in the returned dictionary.

Example:
    ```python
    from bitbully import BitBully, Board

    agent = BitBully()
    board = Board()
    scores = agent.score_all_moves(board)
    assert scores == {3: 1, 2: 0, 4: 0, 1: -1, 5: -1, 0: -2, 6: -2}  # Center column is best on an empty board
```

Example:
    When a column is full, it is omitted from the results:
    ```python
    from bitbully import BitBully, Board

    agent = BitBully()
    board = Board(6 * "3")  # fill center column
    scores = agent.score_all_moves(board)
    assert scores == {2: 1, 4: 1, 1: 0, 5: 0, 0: -1, 6: -1}  # Column 3 is full and thus omitted
```

Definition at line 211 of file solver.py.

◆ score_move()

int bitbully.solver.BitBully.score_move ( self,
Board board,
int column,
int first_guess = 0 )
Evaluate a single move for the given board state.

Args:
    board (Board): The current board state.
    column (int): Column index (0-6) of the move to evaluate.
    first_guess (int): Initial guess for the score (often 0).

Returns:
    int: The evaluation score of the move.

Example:
    ```python
    from bitbully import BitBully, Board

    agent = BitBully()
    board = Board()
    score = agent.score_move(board, column=3)
    assert score == 1  # Score for the center column on an empty board
    ```

Raises:
    ValueError: If the column is outside the valid range or if the column is full.

Notes:
    - This is a wrapper around
      [`bitbully_core.BitBullyCore.scoreMove`][src.bitbully.bitbully_core.BitBullyCore.scoreMove].

Definition at line 178 of file solver.py.

Member Data Documentation

◆ _core

bitbully.solver.BitBully._core = bitbully_core.BitBullyCore()
protected

Definition at line 113 of file solver.py.

◆ opening_book_type

OpeningBookName | None bitbully.solver.BitBully.opening_book_type = opening_book

Definition at line 110 of file solver.py.


The documentation for this class was generated from the following file: