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,
"BitBully")
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,
"Board")
48 .def(
"__str__", &B::toString)
49 .def(
"__repr__", &B::toString)
50 .def(
"playMoveFastBB", &B::playMoveFastBB,
51 "Play a move on the board (bitboard representation)", py::arg(
"mv"))
52 .def(
"canWin", py::overload_cast<int>(&B::canWin, py::const_),
53 "Check, if current player can win by moving into column.",
55 .def(
"canWin", py::overload_cast<>(&B::canWin, py::const_),
56 "Check, if current player can win with the next move.")
57 .def(
"hash", py::overload_cast<>(&B::hash, py::const_),
58 "Hash the current position and return hash value.")
59 .def(
"hasWin", &B::hasWin,
60 "Check, if the player who performed the last move has a winning "
61 "position (4 in a row).")
62 .def(
"playMove", py::overload_cast<int>(&B::playMove),
63 "Play a move by column index", py::arg(
"column"))
64 .def(
"playMoveOnCopy", &B::playMoveOnCopy,
65 "Play a move on a copy of the board and return the new board",
67 .def(
"generateMoves", &B::generateMoves,
"Generate possible moves")
68 .def(
"isLegalMove", &B::isLegalMove,
"Check if a move is legal",
70 .def(
"toString", &B::toString,
71 "Return a string representation of the board")
72 .def(
"movesLeft", &B::movesLeft,
"Get the number of moves left")
73 .def(
"countTokens", &B::countTokens,
74 "Get the number of Tokens on the board")
75 .def(
"mirror", &B::mirror,
"Get the mirrored board")
76 .def(
"sortMoves", &B::sortMoves,
"Sort moves based on priority",
78 .def(
"allPositions", &B::allPositions,
79 "Generate all positions that can be reached from the current board "
81 py::arg(
"upToNPly"), py::arg(
"exactlyN"))
82 .def(
"findThreats", &B::findThreats,
"Find threats on the board",
84 .def(
"generateNonLosingMoves", &B::generateNonLosingMoves,
85 "Generate non-losing moves")
86 .def(
"doubleThreat", &B::doubleThreat,
"Find double threats",
88 .def(
"toArray", &B::toArray,
89 "Convert the board to a 2D array representation")
90 .def(
"setBoard", py::overload_cast<
const std::vector<int>&>(&B::setBoard),
91 "Set the board using a 2D array", py::arg(
"moveSequence"))
92 .def(
"setBoard", py::overload_cast<const B::TBoardArray&>(&B::setBoard),
93 "Set the board using a 2D array", py::arg(
"moveSequence"))
94 .def_static(
"isValid", &B::isValid,
"Check, if a board is a valid one.",
96 .def_static(
"randomBoard", &B::randomBoard,
97 "Create a random board with n tokens.", py::arg(
"nPly"),
98 py::arg(
"forbidDirectWin"))
99 .def(
"toHuffman", &B::toHuffman,
100 "Encode position into a huffman-code compressed sequence.")
101 .def(
"uid", &B::uid,
"Get the unique identifier for the board")
102 .def(
"__eq__", &B::operator==,
"Check if two boards are equal")
103 .def(
"__ne__", &B::operator!=,
"Check if two boards are not equal");
106 py::class_<BitBully::OpeningBook>(m,
"OpeningBook")
108 .def(py::init<const std::filesystem::path&, bool, bool>(),
109 py::arg(
"bookPath"), py::arg(
"is_8ply"), py::arg(
"with_distances"),
110 "Initialize an OpeningBook with explicit settings.")
111 .def(py::init<const std::filesystem::path&>(), py::arg(
"bookPath"),
112 "Initialize an OpeningBook by inferring database type from file "
116 .def(
"init", &BitBully::OpeningBook::init, py::arg(
"bookPath"),
117 py::arg(
"is_8ply"), py::arg(
"with_distances"),
118 "Reinitialize the OpeningBook with new settings.")
119 .def(
"getEntry", &BitBully::OpeningBook::getEntry, py::arg(
"entryIdx"),
120 "Get an entry from the book by index.")
121 .def(
"getBook", &BitBully::OpeningBook::getBook,
122 "Return the raw book table.")
123 .def(
"getBookSize", &BitBully::OpeningBook::getBookSize,
124 "Get the size of the book.")
125 .def(
"getBoardValue", &BitBully::OpeningBook::getBoardValue,
126 py::arg(
"board"),
"Get the value of a given board.")
127 .def(
"isInBook", &BitBully::OpeningBook::isInBook, py::arg(
"board"),
128 "Check, if the given board is in the opening book. Note, that "
129 "usually boards are only present in one mirrored variant.")
130 .def(
"convertValue", &BitBully::OpeningBook::convertValue,
131 py::arg(
"value"), py::arg(
"board"),
132 "Convert a value to the internal scoring system.")
133 .def(
"getNPly", &BitBully::OpeningBook::getNPly,
134 "Get the ply depth of the book.")
137 .def_static(
"readBook", &BitBully::OpeningBook::readBook,
138 py::arg(
"filename"), py::arg(
"with_distances") =
true,
139 py::arg(
"is_8ply") =
false,
"Read a book from a file.");