aboutsummaryrefslogtreecommitdiff
path: root/src/book.cpp
blob: e1cd1270ce8c2619464b5e1b4b60f983026a08df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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; }