aboutsummaryrefslogtreecommitdiff
path: root/src/book.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/book.cpp')
-rw-r--r--src/book.cpp149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/book.cpp b/src/book.cpp
new file mode 100644
index 0000000..e1cd127
--- /dev/null
+++ b/src/book.cpp
@@ -0,0 +1,149 @@
+#include "book.hpp"
+
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <random>
+#include <vector>
+
+#include "board/board.hpp"
+
+constexpr int BOOK_DEPTH = 16;
+
+struct BookEntry {
+ uint64_t key;
+ uint16_t move;
+ uint16_t weight;
+ uint32_t learn;
+};
+
+static std::vector<BookEntry> book;
+
+static std::mt19937 rng{std::random_device{}()};
+
+void InitBook(const std::string &path) {
+ std::ifstream file(path, std::ios::binary);
+ if (!file.is_open()) {
+ std::cerr << "info string Could not open book file: " << path << "\n";
+ return;
+ }
+
+ file.seekg(0, std::ios::end);
+ size_t fileSize = static_cast<size_t>(file.tellg());
+ file.seekg(0, std::ios::beg);
+
+ if (fileSize == 0 || fileSize % sizeof(BookEntry) != 0) {
+ std::cerr << "info string Invalid book file size\n";
+ return;
+ }
+
+ size_t numEntries = fileSize / sizeof(BookEntry);
+ book.resize(numEntries);
+ file.read(reinterpret_cast<char *>(book.data()),
+ static_cast<std::streamsize>(fileSize));
+
+ if (!file) {
+ std::cerr << "info string Failed to read book file\n";
+ book.clear();
+ return;
+ }
+
+ // Byteswap if needed (Polyglot books are big-endian)
+ for (auto &entry : book) {
+ uint64_t k = entry.key;
+ entry.key = ((k & 0x00000000000000FFULL) << 56) |
+ ((k & 0x000000000000FF00ULL) << 40) |
+ ((k & 0x0000000000FF0000ULL) << 24) |
+ ((k & 0x00000000FF000000ULL) << 8) |
+ ((k & 0x000000FF00000000ULL) >> 8) |
+ ((k & 0x0000FF0000000000ULL) >> 24) |
+ ((k & 0x00FF000000000000ULL) >> 40) |
+ ((k & 0xFF00000000000000ULL) >> 56);
+
+ uint16_t m = entry.move;
+ entry.move = static_cast<uint16_t>((m >> 8) | (m << 8));
+
+ uint16_t w = entry.weight;
+ entry.weight = static_cast<uint16_t>((w >> 8) | (w << 8));
+
+ uint32_t l = entry.learn;
+ entry.learn = ((l & 0x000000FFU) << 24) | ((l & 0x0000FF00U) << 8) |
+ ((l & 0x00FF0000U) >> 8) | ((l & 0xFF000000U) >> 24);
+ }
+
+ std::cerr << "info string Book loaded: " << book.size() << " entries\n";
+}
+
+// Convert Polyglot move to Mono internal move format.
+// Polyglot: bits 0-5=to, 6-11=from, 12-14=promo (0=N,1=B,2=R,3=Q)
+// Mono: bits 0-1=promo (00=N,11=B,01=R,10=Q), 2-7=from, 8-13=to
+static uint16_t PolyglotToMono(uint16_t polyglotMove) {
+ uint8_t to = polyglotMove & 63;
+ uint8_t from = (polyglotMove >> 6) & 63;
+ uint8_t promo = (polyglotMove >> 12) & 7;
+
+ PieceType promotion = NONEPIECE;
+ switch (promo) {
+ case 0:
+ promotion = KNIGHT;
+ break;
+ case 1:
+ promotion = BISHOP;
+ break;
+ case 2:
+ promotion = ROOK;
+ break;
+ case 3:
+ promotion = QUEEN;
+ break;
+ default:
+ break;
+ }
+
+ return CreateMove(from, to, promotion);
+}
+
+uint16_t ProbeBook(uint64_t key) {
+ if (book.empty()) {
+ return 0;
+ }
+
+ // Binary search for first matching entry
+ auto it = std::lower_bound(
+ book.begin(), book.end(), key,
+ [](const BookEntry &a, uint64_t k) { return a.key < k; });
+
+ // Collect all matching entries
+ std::vector<const BookEntry *> matches;
+ while (it != book.end() && it->key == key) {
+ matches.push_back(&(*it));
+ ++it;
+ }
+
+ if (matches.empty()) {
+ return 0;
+ }
+
+ // Weighted random selection
+ int totalWeight = 0;
+ for (const auto *e : matches) {
+ totalWeight += e->weight;
+ }
+
+ std::uniform_int_distribution<int> dist(0, totalWeight - 1);
+ int r = dist(rng);
+
+ for (const auto *e : matches) {
+ r -= e->weight;
+ if (r < 0) {
+ return PolyglotToMono(e->move);
+ }
+ }
+
+ return PolyglotToMono(matches.back()->move);
+}
+
+int GetBookDepth() { return BOOK_DEPTH; }