#if defined (EVAL_LEARN) #include "../misc.h" #include "../position.h" #include #include #include // std::memset() using namespace std; // ----------------------------------- // stage compression/decompression // ----------------------------------- // Class that handles bitstream // useful when doing aspect encoding struct BitStream { // Set the memory to store the data in advance. // Assume that memory is cleared to 0. void set_data(uint8_t* data_) { data = data_; reset(); } // Get the pointer passed in set_data(). uint8_t* get_data() const { return data; } // Get the cursor. int get_cursor() const { return bit_cursor; } // reset the cursor void reset() { bit_cursor = 0; } // Write 1bit to the stream. // If b is non-zero, write out 1. If 0, write 0. void write_one_bit(int b) { if (b) data[bit_cursor / 8] |= 1 << (bit_cursor & 7); ++bit_cursor; } // Get 1 bit from the stream. int read_one_bit() { int b = (data[bit_cursor / 8] >> (bit_cursor & 7)) & 1; ++bit_cursor; return b; } // write n bits of data // Data shall be written out from the lower order of d. void write_n_bit(int d, int n) { for (int i = 0; i = RANK_1; --r) { for (File f = FILE_A; f <= FILE_H; ++f) { Piece pc = pos.piece_on(make_square(f, r)); if (type_of(pc) == KING) continue; write_board_piece_to_stream(pc); } } // TODO(someone): Support chess960. stream.write_one_bit(pos.can_castle(WHITE_OO)); stream.write_one_bit(pos.can_castle(WHITE_OOO)); stream.write_one_bit(pos.can_castle(BLACK_OO)); stream.write_one_bit(pos.can_castle(BLACK_OOO)); if (pos.ep_square() == SQ_NONE) { stream.write_one_bit(0); } else { stream.write_one_bit(1); stream.write_n_bit(static_cast(pos.ep_square()), 6); } stream.write_n_bit(pos.state()->rule50, 6); stream.write_n_bit(1 + (pos.game_ply()-(pos.side_to_move() == BLACK)) / 2, 8); assert(stream.get_cursor() <= 256); } // sfen packed by pack() (256bit = 32bytes) // Or sfen to decode with unpack() uint8_t *data; // uint8_t[32]; //private: // Position::set_from_packed_sfen(uint8_t data[32]) I want to use these functions, so the line is bad, but I want to keep it public. BitStream stream; // Output the board pieces to stream. void write_board_piece_to_stream(Piece pc) { // piece type PieceType pr = type_of(pc); auto c = huffman_table[pr]; stream.write_n_bit(c.code, c.bits); if (pc == NO_PIECE) return; // first and second flag stream.write_one_bit(color_of(pc)); } // Read one board piece from stream Piece read_board_piece_from_stream() { PieceType pr = NO_PIECE_TYPE; int code = 0, bits = 0; while (true) { code |= stream.read_one_bit() << bits; ++bits; assert(bits <= 6); for (pr = NO_PIECE_TYPE; pr = RANK_1; --r) { for (File f = FILE_A; f <= FILE_H; ++f) { auto sq = make_square(f, r); if (mirror) { sq = flip_file(sq); } // it seems there are already balls Piece pc; if (type_of(board[sq]) != KING) { assert(board[sq] == NO_PIECE); pc = packer.read_board_piece_from_stream(); } else { pc = board[sq]; board[sq] = NO_PIECE; // put_piece() will catch ASSERT unless you remove it all. } // There may be no pieces, so skip in that case. if (pc == NO_PIECE) continue; put_piece(Piece(pc), sq); //cout << sq << ' ' << board[sq] << ' ' << stream.get_cursor() << endl; if (stream.get_cursor()> 256) return 1; //assert(stream.get_cursor() <= 256); } } // Castling availability. // TODO(someone): Support chess960. st->castlingRights = 0; if (stream.read_one_bit()) { Square rsq; for (rsq = relative_square(WHITE, SQ_H1); piece_on(rsq) != W_ROOK; --rsq) {} set_castling_right(WHITE, rsq); } if (stream.read_one_bit()) { Square rsq; for (rsq = relative_square(WHITE, SQ_A1); piece_on(rsq) != W_ROOK; ++rsq) {} set_castling_right(WHITE, rsq); } if (stream.read_one_bit()) { Square rsq; for (rsq = relative_square(BLACK, SQ_H1); piece_on(rsq) != B_ROOK; --rsq) {} set_castling_right(BLACK, rsq); } if (stream.read_one_bit()) { Square rsq; for (rsq = relative_square(BLACK, SQ_A1); piece_on(rsq) != B_ROOK; ++rsq) {} set_castling_right(BLACK, rsq); } // En passant square. Ignore if no pawn capture is possible if (stream.read_one_bit()) { Square ep_square = static_cast(stream.read_n_bit(6)); if (mirror) { ep_square = flip_file(ep_square); } st->epSquare = ep_square; if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN)) || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))) st->epSquare = SQ_NONE; } else { st->epSquare = SQ_NONE; } // Halfmove clock st->rule50 = static_cast(stream.read_n_bit(6)); // Fullmove number gamePly = static_cast(stream.read_n_bit(8)); // Convert from fullmove starting from 1 to gamePly starting from 0, // handle also common incorrect FEN with fullmove = 0. gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK); assert(stream.get_cursor() <= 256); chess960 = false; thisThread = th; set_state(st); //std::cout << *this << std::endl; assert(pos_is_ok()); return 0; } // Give the board, hand piece, and turn, and return the sfen. //std::string Position::sfen_from_rawdata(Piece board[81], Hand hands[2], Color turn, int gamePly_) //{ // // Copy it to an internal structure and call sfen() if the conversion process depends only on it // // Maybe it will be converted normally... // Position pos; // // memcpy(pos.board, board, sizeof(Piece) * 81); // memcpy(pos.hand, hands, sizeof(Hand) * 2); // pos.sideToMove = turn; // pos.gamePly = gamePly_; // // return pos.sfen(); // // // Implementation of ↑ is beautiful, but slow. // // This is a bottleneck when learning a large amount of game records, so write a function to unpack directly. //} // Get the packed sfen. Returns to the buffer specified in the argument. void Position::sfen_pack(PackedSfen& sfen) { SfenPacker sp; sp.data = (uint8_t*)&sfen; sp.pack(*this); } //// Unpack the packed sfen. Returns an sfen string. //std::string Position::sfen_unpack(const PackedSfen& sfen) //{ // SfenPacker sp; // sp.data = (uint8_t*)&sfen; // return sp.unpack(); //} #endif // USE_SFEN_PACKER