LCOV - code coverage report
Current view: top level - Source/nnue/learn - learn_tools.cpp (source / functions) Coverage Total Hit
Test: coverage Lines: 0.0 % 134 0
Test Date: 2026-03-02 16:42:41 Functions: 0.0 % 9 0

            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
        

Generated by: LCOV version 2.0-1