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