Line data Source code
1 : #include "learn_tools.hpp"
2 :
3 : #include <algorithm>
4 : #include <cstring>
5 : #include <fstream>
6 : #include <iostream>
7 : #include <regex>
8 : #include <sstream>
9 : #include <string>
10 : #include <vector>
11 :
12 : #include "definition.hpp"
13 : #include "logging.hpp"
14 : #include "material.hpp"
15 : #include "moveGen.hpp"
16 : #include "pieceTools.hpp"
17 : #include "position.hpp"
18 : #include "positionTools.hpp"
19 :
20 : #if defined(WITH_DATA2BIN)
21 :
22 : // Mostly copy/paste from nodchip Stockfish repository and adapted to Minic
23 : // Tools for handling various learning data format
24 :
25 : namespace { // anonymous
26 :
27 : // Class that handles bitstream
28 : // useful when doing aspect encoding
29 : struct BitStream {
30 : // Set the memory to store the data in advance.
31 : // Assume that memory is cleared to 0.
32 : void set_data(uint8_t* data_) {
33 0 : data = data_;
34 : reset();
35 : }
36 :
37 : // Get the pointer passed in set_data().
38 : uint8_t* get_data() const { return data; }
39 :
40 : // Get the cursor.
41 0 : int get_cursor() const { return bit_cursor; }
42 :
43 : // reset the cursor
44 0 : void reset() { bit_cursor = 0; }
45 :
46 : // Write 1bit to the stream.
47 : // If b is non-zero, write out 1. If 0, write 0.
48 0 : void write_one_bit(int b) {
49 0 : if (b) {
50 : //std::cout << bit_cursor << std::endl;
51 0 : data[bit_cursor / 8] |= static_cast<uint8_t>(1 << (bit_cursor & 7));
52 : }
53 0 : ++bit_cursor;
54 0 : assert(bit_cursor <= 256);
55 0 : }
56 :
57 : // Get 1 bit from the stream.
58 0 : int read_one_bit() {
59 0 : int b = (data[bit_cursor / 8] >> (bit_cursor & 7)) & 1;
60 0 : ++bit_cursor;
61 0 : assert(bit_cursor <= 256);
62 0 : return b;
63 : }
64 :
65 : // write n bits of data
66 : // Data shall be written out from the lower order of d.
67 : void write_n_bit(int d, int n) {
68 0 : for (int i = 0; i < n; ++i) write_one_bit(d & (1 << i));
69 : }
70 :
71 : // read n bits of data
72 : // Reverse conversion of write_n_bit().
73 : int read_n_bit(int n) {
74 : int result = 0;
75 0 : for (int i = 0; i < n; ++i) result |= read_one_bit() ? (1 << i) : 0;
76 :
77 : return result;
78 : }
79 :
80 : private:
81 : // Next bit position to read/write.
82 : int bit_cursor = 0;
83 :
84 : // data entity
85 : mutable uint8_t* data = nullptr;
86 : };
87 :
88 : struct HuffmanedPiece {
89 : int code; // how it will be coded
90 : int bits; // How many bits do you have
91 : };
92 :
93 : HuffmanedPiece huffman_table[] = {
94 : {0b0000, 1}, // NO_PIECE
95 : {0b0001, 4}, // PAWN
96 : {0b0011, 4}, // KNIGHT
97 : {0b0101, 4}, // BISHOP
98 : {0b0111, 4}, // ROOK
99 : {0b1001, 4}, // QUEEN
100 : };
101 :
102 : // Class for compressing/decompressing sfen
103 : struct SfenPacker {
104 : // Pack sfen and store in data[32].
105 0 : void pack(const Position& pos) {
106 : //std::cout << GetFEN(pos) << std::endl;
107 :
108 0 : memset(data, 0, 32 /* 256bit */);
109 0 : stream.set_data(data);
110 :
111 : // Side to move.
112 0 : stream.write_one_bit(static_cast<int>(pos.c));
113 :
114 : // 7-bit positions for leading and trailing balls
115 : // White king and black king, 6 bits for each.
116 0 : for (auto c : {Co_White, Co_Black}) stream.write_n_bit(pos.king[c], 6);
117 :
118 : // Write the pieces on the board other than the kings.
119 : for (Rank r = Rank_8;; --r) {
120 0 : for (File f = File_a; f <= File_h; ++f) {
121 0 : Piece pc = pos.board_const(MakeSquare(f, r));
122 : //std::cout << static_cast<int>(pc) << std::endl;
123 0 : if (std::abs(pc) == P_wk) continue;
124 0 : write_board_piece_to_stream(pc);
125 : }
126 0 : if (r == Rank_1) break; // because rank is unsigned !!
127 : }
128 :
129 : ///@todo: Support chess960.
130 0 : stream.write_one_bit(pos.castling & C_wks);
131 0 : stream.write_one_bit(pos.castling & C_wqs);
132 0 : stream.write_one_bit(pos.castling & C_bks);
133 0 : stream.write_one_bit(pos.castling & C_bqs);
134 :
135 0 : if (pos.ep == INVALIDSQUARE) { stream.write_one_bit(0); }
136 : else {
137 0 : stream.write_one_bit(1);
138 0 : stream.write_n_bit(static_cast<int>(pos.ep), 6);
139 : }
140 :
141 0 : stream.write_n_bit(pos.fifty, 6);
142 0 : stream.write_n_bit(pos.moves, 8);
143 0 : assert(stream.get_cursor() <= 256);
144 0 : }
145 :
146 : // sfen packed by pack() (256bit = 32bytes)
147 : // Or sfen to decode with unpack()
148 : uint8_t* data = nullptr; // uint8_t[32];
149 :
150 : BitStream stream;
151 :
152 : // Output the board pieces to stream.
153 0 : void write_board_piece_to_stream(Piece pc) {
154 : // piece type
155 0 : auto c = huffman_table[std::abs(pc)];
156 0 : stream.write_n_bit(c.code, c.bits);
157 0 : if (pc == P_none) return;
158 0 : stream.write_one_bit(pc > 0 ? Co_White : Co_Black);
159 : }
160 :
161 : // Read one board piece from stream
162 0 : Piece read_board_piece_from_stream() {
163 : Piece pr = P_none;
164 : int code = 0;
165 : int bits = 0;
166 : while (true) {
167 0 : code |= stream.read_one_bit() << bits;
168 0 : ++bits;
169 0 : assert(bits <= 6);
170 0 : for (pr = P_none; pr < P_wk; ++pr) {
171 0 : if (huffman_table[pr].code == code && huffman_table[pr].bits == bits) goto Found;
172 : }
173 : }
174 : Found:;
175 : assert(pr != P_wk);
176 0 : if (pr == P_none) return P_none;
177 :
178 0 : const Color c = static_cast<Color>(stream.read_one_bit());
179 0 : return c == Co_White ? pr : ~pr;
180 : }
181 :
182 : }; // SfenPacker
183 :
184 : } // namespace
185 :
186 0 : MiniMove ToSFMove(const Position& p, const Square from, const Square to, const MType type) {
187 0 : assert(from >= 0 && from < 64);
188 0 : assert(to >= 0 && to < 64);
189 0 : if (isPromotion(type)) return FromSF::MakeMove<FromSF::PROMOTION>(from, to, promShift(type));
190 0 : else if (type == T_ep)
191 0 : return FromSF::MakeMove<FromSF::ENPASSANT>(from, p.ep);
192 0 : else if (isCastling(type)) {
193 0 : switch (type) {
194 0 : case T_wks: return FromSF::MakeMove<FromSF::CASTLING>(from, p.rootInfo().rooksInit[Co_White][CT_OO]);
195 0 : case T_wqs: return FromSF::MakeMove<FromSF::CASTLING>(from, p.rootInfo().rooksInit[Co_White][CT_OOO]);
196 0 : case T_bks: return FromSF::MakeMove<FromSF::CASTLING>(from, p.rootInfo().rooksInit[Co_Black][CT_OO]);
197 0 : case T_bqs: return FromSF::MakeMove<FromSF::CASTLING>(from, p.rootInfo().rooksInit[Co_Black][CT_OOO]);
198 : default: return INVALIDMINIMOVE;
199 : }
200 : }
201 : else
202 0 : return FromSF::MakeMoveStd(from, to);
203 : }
204 :
205 0 : MiniMove FromSFMove(const Position& p, const MiniMove sfmove) {
206 0 : const Square from = FromSF::from_sq(sfmove);
207 : const Square to = FromSF::to_sq(sfmove);
208 : const FromSF::MoveType SFtype = FromSF::type_of(sfmove);
209 : MType type = T_std;
210 0 : switch (SFtype) {
211 0 : case FromSF::NORMAL:
212 0 : if (p.board_const(to) != P_none) type = T_capture;
213 : break;
214 0 : case FromSF::PROMOTION:
215 : {
216 : const Piece pp = FromSF::promotion_type(sfmove);
217 0 : if (pp == P_wq) { type = p.board_const(to) == P_none ? T_promq : T_cappromq; }
218 0 : else if (pp == P_wr) {
219 0 : type = p.board_const(to) == P_none ? T_promr : T_cappromr;
220 : }
221 0 : else if (pp == P_wb) {
222 0 : type = p.board_const(to) == P_none ? T_promb : T_cappromb;
223 : }
224 : else if (pp == P_wn) {
225 0 : type = p.board_const(to) == P_none ? T_promn : T_cappromn;
226 : }
227 : }
228 : break;
229 : case FromSF::ENPASSANT: type = T_ep; break;
230 : case FromSF::CASTLING:
231 0 : if (to == p.rootInfo().rooksInit[Co_White][CT_OO]) type = T_wks;
232 0 : if (to == p.rootInfo().rooksInit[Co_White][CT_OOO]) type = T_wqs;
233 0 : if (to == p.rootInfo().rooksInit[Co_Black][CT_OO]) type = T_bks;
234 0 : if (to == p.rootInfo().rooksInit[Co_Black][CT_OOO]) type = T_bqs;
235 : break;
236 0 : default: assert(false);
237 : }
238 0 : return ToMove(from, to, type);
239 : }
240 :
241 0 : void sfen_pack(const Position& p, PackedSfen& sfen) {
242 0 : SfenPacker sp;
243 0 : sp.data = reinterpret_cast<uint8_t*>(&sfen);
244 0 : sp.pack(p);
245 0 : }
246 :
247 : // If there is a problem with the passed phase and there is an error, non-zero is returned.
248 0 : int set_from_packed_sfen(Position& p, PackedSfen& sfen) {
249 0 : SfenPacker packer;
250 : auto& stream = packer.stream;
251 : stream.set_data(reinterpret_cast<uint8_t*>(&sfen));
252 :
253 : // Active color
254 0 : p.c = static_cast<Color>(stream.read_one_bit());
255 :
256 0 : for (auto c : {Co_White, Co_Black}) {
257 0 : const Square sq = static_cast<Square>(stream.read_n_bit(6));
258 0 : p.board(sq) = (c == Co_White ? P_wk : P_bk);
259 0 : p.king[c] = sq;
260 : }
261 :
262 0 : p.rootInfo().kingInit[Co_White] = p.king[Co_White];
263 0 : p.rootInfo().kingInit[Co_Black] = p.king[Co_Black];
264 :
265 : // Piece placement
266 : for (Rank r = Rank_8;; --r) {
267 0 : for (File f = File_a; f <= File_h; ++f) {
268 : auto sq = MakeSquare(f, r);
269 : Piece pc = P_none;
270 :
271 : // skip already given kings
272 0 : if (PieceTools::getPieceType(p, sq) != P_wk) {
273 0 : assert(p.board_const(sq) == P_none);
274 0 : pc = packer.read_board_piece_from_stream();
275 : }
276 :
277 : // There may be no pieces, so skip in that case (also cover king case).
278 0 : if (pc == P_none) continue;
279 :
280 0 : p.board(sq) = pc;
281 :
282 0 : if (stream.get_cursor() > 256) return 1;
283 : }
284 0 : if (r == Rank_1) break; // because rank is unsigned !!
285 : }
286 :
287 : // Castling availability.
288 0 : p.castling = C_none;
289 0 : if (stream.read_one_bit()) {
290 : Square rsq;
291 0 : for (rsq = relative_square(Co_White, Sq_h1); p.board_const(rsq) != P_wr; --rsq) {}
292 0 : p.rootInfo().rooksInit[Co_White][CT_OO] = rsq;
293 : p.castling |= C_wks;
294 : }
295 0 : if (stream.read_one_bit()) {
296 : Square rsq;
297 0 : for (rsq = relative_square(Co_White, Sq_a1); p.board_const(rsq) != P_wr; ++rsq) {}
298 0 : p.rootInfo().rooksInit[Co_White][CT_OOO] = rsq;
299 : p.castling |= C_wqs;
300 : }
301 0 : if (stream.read_one_bit()) {
302 : Square rsq;
303 0 : for (rsq = relative_square(Co_Black, Sq_h1); p.board_const(rsq) != P_br; --rsq) {}
304 0 : p.rootInfo().rooksInit[Co_Black][CT_OO] = rsq;
305 : p.castling |= C_bks;
306 : }
307 0 : if (stream.read_one_bit()) {
308 : Square rsq;
309 0 : for (rsq = relative_square(Co_Black, Sq_a1); p.board_const(rsq) != P_br; ++rsq) {}
310 0 : p.rootInfo().rooksInit[Co_Black][CT_OOO] = rsq;
311 : p.castling |= C_bqs;
312 : }
313 :
314 : // En passant square.
315 0 : if (stream.read_one_bit()) {
316 0 : Square ep_square = static_cast<Square>(stream.read_n_bit(6));
317 0 : p.ep = ep_square;
318 : ///@todo verify en passant square
319 : }
320 : else
321 0 : p.ep = INVALIDSQUARE;
322 :
323 : // Halfmove clock
324 0 : p.fifty = static_cast<decltype(p.fifty)>(stream.read_n_bit(6));
325 :
326 : // Fullmove number
327 0 : p.moves = static_cast<decltype(p.moves)>(stream.read_n_bit(8));
328 0 : if (p.moves < 1) { // fix a LittleBlitzer bug here ...
329 0 : Logging::LogIt(Logging::logDebug) << "Wrong move counter " << static_cast<int>(p.moves) << " using 1 instead";
330 0 : p.moves = 1;
331 : }
332 0 : assert(stream.get_cursor() <= 256);
333 :
334 0 : p.halfmoves = static_cast<decltype(p.halfmoves)>((p.moves - 1) * 2 + 1 + (p.c == Co_Black ? 1 : 0));
335 :
336 0 : p.rootInfo().initCaslingPermHashTable();
337 :
338 0 : BBTools::setBitBoards(p);
339 0 : MaterialHash::initMaterial(p);
340 0 : p.h = computeHash(p);
341 0 : p.ph = computePHash(p);
342 :
343 0 : return 0;
344 : }
345 :
346 : #endif // WITH_DATA2BIN
|