1#include <pybind11/pybind11.h>
2#include <pybind11/stl.h>
3#include <pybind11/stl/filesystem.h>
11#include "OpeningBook.h"
13namespace py = pybind11;
16PYBIND11_MODULE(bitbully_core, m) {
18 "Bitbully is a fast Connect-4 solver.";
20 py::class_<BitBully::BitBully>(m,
"BitBullyCore")
22 .def(py::init<std::filesystem::path>(), py::arg(
"openingBookPath"))
23 .def(
"mtdf", &BitBully::BitBully::mtdf,
"MTD(f) algorithm",
24 py::arg(
"board"), py::arg(
"first_guess"))
25 .def(
"nullWindow", &BitBully::BitBully::nullWindow,
"Null-window search",
27 .def(
"negamax", &BitBully::BitBully::negamax,
"negamax search",
28 py::arg(
"board"), py::arg(
"alpha"), py::arg(
"beta"),
30 .def(
"scoreMoves", &BitBully::BitBully::scoreMoves,
"evaluate all moves",
32 .def(
"resetTranspositionTable",
33 &BitBully::BitBully::resetTranspositionTable,
34 "Reset the transposition table")
35 .def(
"getNodeCounter", &BitBully::BitBully::getNodeCounter,
36 "Get the current node counter")
37 .def(
"resetNodeCounter", &BitBully::BitBully::resetNodeCounter,
38 "Reset the node counter")
39 .def(
"isBookLoaded", &BitBully::BitBully::isBookLoaded,
40 "Check, if opening book is loaded")
41 .def(
"isBookLoaded", &BitBully::BitBully::isBookLoaded,
42 "Check, if opening book is loaded");
46 py::class_<B>(m,
"BoardCore")
48 .def(py::init<const B&>())
49 .def(
"__str__", &B::toString)
50 .def(
"__repr__", &B::toString)
51 .def(
"playMoveFastBB", &B::playMoveFastBB,
52 "Play a move on the board (bitboard representation)", py::arg(
"mv"))
53 .def(
"canWin", py::overload_cast<int>(&B::canWin, py::const_),
54 "Check, if current player can win by moving into column.",
56 .def(
"copy", &B::copy,
"Create a deep copy of the board.")
57 .def(
"canWin", py::overload_cast<>(&B::canWin, py::const_),
58 "Check, if current player can win with the next move.")
59 .def(
"hash", py::overload_cast<>(&B::hash, py::const_),
60 "Hash the current position and return hash value.")
61 .def(
"hasWin", &B::hasWin,
62 "Check, if the player who performed the last move has a winning "
63 "position (4 in a row).")
64 .def(
"playMove", py::overload_cast<int>(&B::playMove),
65 "Play a move by column index", py::arg(
"column"))
66 .def(
"playMoveOnCopy", &B::playMoveOnCopy,
67 "Play a move on a copy of the board and return the new board",
69 .def(
"generateMoves", &B::generateMoves,
"Generate possible moves")
70 .def(
"isLegalMove", &B::isLegalMove,
"Check if a move is legal",
72 .def(
"toString", &B::toString,
73 "Return a string representation of the board")
74 .def(
"movesLeft", &B::movesLeft,
"Get the number of moves left")
75 .def(
"countTokens", &B::countTokens,
76 "Get the number of Tokens on the board")
77 .def(
"mirror", &B::mirror,
78 "Get the mirrored board (mirror around center column)")
79 .def(
"sortMoves", &B::sortMoves,
"Sort moves based on priority",
81 .def(
"allPositions", &B::allPositions,
82 "Generate all positions that can be reached from the current board "
84 py::arg(
"upToNPly"), py::arg(
"exactlyN"))
85 .def(
"findThreats", &B::findThreats,
"Find threats on the board",
87 .def(
"generateNonLosingMoves", &B::generateNonLosingMoves,
88 "Generate non-losing moves")
89 .def(
"doubleThreat", &B::doubleThreat,
"Find double threats",
91 .def(
"toArray", &B::toArray,
92 "Convert the board to a 2D array representation")
93 .def(
"setBoard", py::overload_cast<
const std::vector<int>&>(&B::setBoard),
94 "Set the board using a 2D array", py::arg(
"moveSequence"))
95 .def(
"setBoard", py::overload_cast<const B::TBoardArray&>(&B::setBoard),
96 "Set the board using a 2D array", py::arg(
"moveSequence"))
97 .def_static(
"isValid", &B::isValid,
"Check, if a board is a valid one.",
99 .def_static(
"randomBoard", &B::randomBoard,
100 "Create a random board with n tokens.", py::arg(
"nPly"),
101 py::arg(
"forbidDirectWin"))
102 .def(
"toHuffman", &B::toHuffman,
103 "Encode position into a huffman-code compressed sequence.")
104 .def(
"uid", &B::uid,
"Get the unique identifier for the board")
105 .def(
"__eq__", &B::operator==,
"Check if two boards are equal")
106 .def(
"__ne__", &B::operator!=,
"Check if two boards are not equal");
109 py::class_<BitBully::OpeningBook>(m,
"OpeningBookCore")
111 .def(py::init<const std::filesystem::path&, bool, bool>(),
112 py::arg(
"bookPath"), py::arg(
"is_8ply"), py::arg(
"with_distances"),
113 "Initialize an OpeningBook with explicit settings.")
114 .def(py::init<const std::filesystem::path&>(), py::arg(
"bookPath"),
115 "Initialize an OpeningBook by inferring database type from file "
119 .def(
"init", &BitBully::OpeningBook::init, py::arg(
"bookPath"),
120 py::arg(
"is_8ply"), py::arg(
"with_distances"),
121 "Reinitialize the OpeningBook with new settings.")
122 .def(
"getEntry", &BitBully::OpeningBook::getEntry, py::arg(
"entryIdx"),
123 "Get an entry from the book by index.")
124 .def(
"getBook", &BitBully::OpeningBook::getBook,
125 "Return the raw book table.")
126 .def(
"getBookSize", &BitBully::OpeningBook::getBookSize,
127 "Get the size of the book.")
128 .def(
"getBoardValue", &BitBully::OpeningBook::getBoardValue,
129 py::arg(
"board"),
"Get the value of a given board.")
130 .def(
"isInBook", &BitBully::OpeningBook::isInBook, py::arg(
"board"),
131 "Check, if the given board is in the opening book. Note, that "
132 "usually boards are only present in one mirrored variant.")
133 .def(
"convertValue", &BitBully::OpeningBook::convertValue,
134 py::arg(
"value"), py::arg(
"board"),
135 "Convert a value to the internal scoring system.")
136 .def(
"getNPly", &BitBully::OpeningBook::getNPly,
137 "Get the ply depth of the book.")
140 .def_static(
"readBook", &BitBully::OpeningBook::readBook,
141 py::arg(
"filename"), py::arg(
"with_distances") =
true,
142 py::arg(
"is_8ply") =
false,
"Read a book from a file.");