From 399d556c27487dc0e453c0be90b1cc84a1f2ab24 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Fri, 3 Jun 2022 06:36:46 +0200 Subject: [PATCH] Minimal support for FRC in the data generator. (#4049) Allows UCI_Chess960 to be true during data generation. If UCI_Chess960 is true then strips castling rights from all saved positions and skips saving positions with castling move. UCI_Chess960 is respected in transforms. --- src/position.cpp | 8 +++--- src/position.h | 6 ++--- src/tools/convert.cpp | 6 ++--- src/tools/sfen_packer.cpp | 29 ++++++++++++--------- src/tools/sfen_packer.h | 4 +-- src/tools/stats.cpp | 3 ++- src/tools/training_data_generator.cpp | 16 +++++++++--- src/tools/training_data_generator_nonpv.cpp | 21 ++++++++++----- src/tools/transform.cpp | 13 +++++---- 9 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index a8eea677..90b3f401 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1370,15 +1370,15 @@ bool Position::pos_is_ok() const { // Add a function that directly unpacks for speed. It's pretty tough. // Write it by combining packer::unpack() and Position::set(). // If there is a problem with the passed phase and there is an error, non-zero is returned. -int Position::set_from_packed_sfen(const Tools::PackedSfen& sfen , StateInfo* si, Thread* th) +int Position::set_from_packed_sfen(const Tools::PackedSfen& sfen , StateInfo* si, Thread* th, bool frc) { - return Tools::set_from_packed_sfen(*this, sfen, si, th); + return Tools::set_from_packed_sfen(*this, sfen, si, th, frc); } // Get the packed sfen. Returns to the buffer specified in the argument. -void Position::sfen_pack(Tools::PackedSfen& sfen) +void Position::sfen_pack(Tools::PackedSfen& sfen, bool resetCastlingRights) { - sfen = Tools::sfen_pack(*this); + sfen = Tools::sfen_pack(*this, resetCastlingRights); } } // namespace Stockfish diff --git a/src/position.h b/src/position.h index d0025930..7158b5ce 100644 --- a/src/position.h +++ b/src/position.h @@ -179,17 +179,17 @@ public: // --sfenization helper - friend int Tools::set_from_packed_sfen(Position& pos, const Tools::PackedSfen& sfen, StateInfo* si, Thread* th); + friend int Tools::set_from_packed_sfen(Position& pos, const Tools::PackedSfen& sfen, StateInfo* si, Thread* th, bool frc); // Get the packed sfen. Returns to the buffer specified in the argument. // Do not include gamePly in pack. - void sfen_pack(Tools::PackedSfen& sfen); + void sfen_pack(Tools::PackedSfen& sfen, bool resetCastlingRights); // It is slow to go through sfen, so I made a function to set packed sfen directly. // Equivalent to pos.set(sfen_unpack(data),si,th);. // If there is a problem with the passed phase and there is an error, non-zero is returned. // PackedSfen does not include gamePly so it cannot be restored. If you want to set it, specify it with an argument. - int set_from_packed_sfen(const Tools::PackedSfen& sfen, StateInfo* si, Thread* th); + int set_from_packed_sfen(const Tools::PackedSfen& sfen, StateInfo* si, Thread* th, bool frc); void clear() { std::memset(this, 0, sizeof(Position)); } diff --git a/src/tools/convert.cpp b/src/tools/convert.cpp index 03f3e4a7..e09399f0 100644 --- a/src/tools/convert.cpp +++ b/src/tools/convert.cpp @@ -103,7 +103,7 @@ namespace Stockfish::Tools filtered_size_fen++; } else { - tpos.sfen_pack(p.sfen); + tpos.sfen_pack(p.sfen, false); } } else if (token == "move") { @@ -337,7 +337,7 @@ namespace Stockfish::Tools StateInfo si; pos.set(str_fen, false, &si, th); - pos.sfen_pack(psv.sfen); + pos.sfen_pack(psv.sfen, false); } #if defined(DEBUG_CONVERT_BIN_FROM_PGN_EXTRACT) @@ -478,7 +478,7 @@ namespace Stockfish::Tools { if (fs.read((char*)&p, sizeof(PackedSfenValue))) { StateInfo si; - tpos.set_from_packed_sfen(p.sfen, &si, th); + tpos.set_from_packed_sfen(p.sfen, &si, th, false); // write as plain text ofs << "fen " << tpos.fen() << std::endl; diff --git a/src/tools/sfen_packer.cpp b/src/tools/sfen_packer.cpp index 7a6fb979..d8be3421 100644 --- a/src/tools/sfen_packer.cpp +++ b/src/tools/sfen_packer.cpp @@ -94,7 +94,7 @@ namespace Stockfish::Tools { // struct SfenPacker { - void pack(const Position& pos); + void pack(const Position& pos, bool resetCastlingRights); // sfen packed by pack() (256bit = 32bytes) // Or sfen to decode with unpack() @@ -149,7 +149,7 @@ namespace Stockfish::Tools { }; // Pack sfen and store in data[32]. - void SfenPacker::pack(const Position& pos) + void SfenPacker::pack(const Position& pos, bool resetCastlingRights) { memset(data, 0, 32 /* 256bit */); stream.set_data(data); @@ -175,11 +175,17 @@ namespace Stockfish::Tools { } } - // 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 (resetCastlingRights) + { + stream.write_n_bit(0, 4); + } + else + { + 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); @@ -249,7 +255,7 @@ namespace Stockfish::Tools { return make_piece(c, pr); } - int set_from_packed_sfen(Position& pos, const PackedSfen& sfen, StateInfo* si, Thread* th) + int set_from_packed_sfen(Position& pos, const PackedSfen& sfen, StateInfo* si, Thread* th, bool frc) { SfenPacker packer; auto& stream = packer.stream; @@ -304,7 +310,6 @@ namespace Stockfish::Tools { } // Castling availability. - // TODO(someone): Support chess960. pos.st->castlingRights = 0; if (stream.read_one_bit()) { Square rsq; @@ -362,7 +367,7 @@ namespace Stockfish::Tools { assert(stream.get_cursor() <= 256); - pos.chess960 = false; + pos.chess960 = frc; pos.thisThread = th; pos.set_state(pos.st); @@ -371,13 +376,13 @@ namespace Stockfish::Tools { return 0; } - PackedSfen sfen_pack(Position& pos) + PackedSfen sfen_pack(Position& pos, bool resetCastlingRights) { PackedSfen sfen; SfenPacker sp; sp.data = (uint8_t*)&sfen; - sp.pack(pos); + sp.pack(pos, resetCastlingRights); return sfen; } diff --git a/src/tools/sfen_packer.h b/src/tools/sfen_packer.h index 42093043..b1dea8e3 100644 --- a/src/tools/sfen_packer.h +++ b/src/tools/sfen_packer.h @@ -15,8 +15,8 @@ namespace Stockfish { namespace Stockfish::Tools { - int set_from_packed_sfen(Position& pos, const PackedSfen& sfen, StateInfo* si, Thread* th); - PackedSfen sfen_pack(Position& pos); + int set_from_packed_sfen(Position& pos, const PackedSfen& sfen, StateInfo* si, Thread* th, bool frc); + PackedSfen sfen_pack(Position& pos, bool resetCastlingRights); } #endif \ No newline at end of file diff --git a/src/tools/stats.cpp b/src/tools/stats.cpp index 6786abd8..bdf240f8 100644 --- a/src/tools/stats.cpp +++ b/src/tools/stats.cpp @@ -1192,6 +1192,7 @@ namespace Stockfish::Tools::Stats Thread* th = Threads.main(); Position& pos = th->rootPos; StateInfo si; + const bool frc = Options["UCI_Chess960"]; auto in = Tools::open_sfen_input_file(filename); @@ -1214,7 +1215,7 @@ namespace Stockfish::Tools::Stats auto& psv = v.value(); - pos.set_from_packed_sfen(psv.sfen, &si, th); + pos.set_from_packed_sfen(psv.sfen, &si, th, frc); on_entry(pos, (Move)psv.move, psv); diff --git a/src/tools/training_data_generator.cpp b/src/tools/training_data_generator.cpp index 437454f1..2c06cfed 100644 --- a/src/tools/training_data_generator.cpp +++ b/src/tools/training_data_generator.cpp @@ -271,6 +271,7 @@ namespace Stockfish::Tools const auto start_time = now(); + const bool frc = Options["UCI_Chess960"]; // repeat until the specified number of times while (!quit) { @@ -286,11 +287,11 @@ namespace Stockfish::Tools if (opening_book != nullptr) { auto& fen = opening_book->next_fen(); - pos.set(fen, false, &si, &th); + pos.set(fen, frc, &si, &th); } else { - pos.set(StartFEN, false, &si, &th); + pos.set(StartFEN, frc, &si, &th); } int resign_counter = 0; @@ -367,7 +368,7 @@ namespace Stockfish::Tools // Here we only write the position data. // Result is added after the whole game is done. - pos.sfen_pack(psv.sfen); + pos.sfen_pack(psv.sfen, pos.is_chess960()); psv.score = search_value; psv.move = search_pv[0]; @@ -695,9 +696,17 @@ namespace Stockfish::Tools it->game_result = side_to_move == result_color ? result : -result; } + const bool frc = th.rootPos.is_chess960(); // Write sfens in move order to make potential compression easier for (auto& sfen : sfens) { + // Skip positions with castling bestmove in FRC so that we don't + // need to support it in the trainer. + if (frc && type_of((Move)sfen.move) == CASTLING) + { + continue; + } + // Return true if there is already enough data generated. const auto iter = counter.fetch_add(1); if (iter >= limit) @@ -858,7 +867,6 @@ namespace Stockfish::Tools else if (token == "set_recommended_uci_options") { UCI::setoption("Skill Level", "20"); - UCI::setoption("UCI_Chess960", "false"); UCI::setoption("UCI_LimitStrength", "false"); UCI::setoption("PruneAtShallowDepth", "false"); UCI::setoption("EnableTranspositionTable", "true"); diff --git a/src/tools/training_data_generator_nonpv.cpp b/src/tools/training_data_generator_nonpv.cpp index 04bab4a2..e28b0b93 100644 --- a/src/tools/training_data_generator_nonpv.cpp +++ b/src/tools/training_data_generator_nonpv.cpp @@ -204,23 +204,24 @@ namespace Stockfish::Tools if ((double)prng.rand() / std::numeric_limits::max() < params.exploration_save_rate) { psv.emplace_back(); - pos.sfen_pack(psv.back().sfen); + pos.sfen_pack(psv.back().sfen, pos.is_chess960()); } }); auto& pos = th.rootPos; StateInfo si; + const bool frc = Options["UCI_Chess960"]; for (int i = 0; i < count; ++i) { if (opening_book != nullptr) { auto& fen = opening_book->next_fen(); - pos.set(fen, false, &si, &th); + pos.set(fen, frc, &si, &th); } else { - pos.set(StartFEN, false, &si, &th); + pos.set(StartFEN, frc, &si, &th); } for(int ply = 0; ply < params.exploration_max_ply; ++ply) @@ -267,6 +268,7 @@ namespace Stockfish::Tools // end flag bool quit = false; + const bool frc = Options["UCI_Chess960"]; // repeat until the specified number of times while (!quit) { @@ -280,7 +282,7 @@ namespace Stockfish::Tools for (auto& ps : packed_sfens) { - pos.set_from_packed_sfen(ps.sfen, &si, &th); + pos.set_from_packed_sfen(ps.sfen, &si, &th, frc); pos.state()->rule50 = 0; if (params.smart_fen_skipping && pos.checkers()) @@ -306,7 +308,7 @@ namespace Stockfish::Tools } auto& new_ps = psv.emplace_back(); - pos.sfen_pack(new_ps.sfen); + pos.sfen_pack(new_ps.sfen, pos.is_chess960()); new_ps.score = search_value; new_ps.move = search_pv[0]; new_ps.gamePly = 1; @@ -329,9 +331,17 @@ namespace Stockfish::Tools std::atomic& counter, uint64_t limit) { + const bool frc = th.rootPos.is_chess960(); // Write sfens in move order to make potential compression easier for (auto& sfen : sfens) { + // Skip positions with castling bestmove in FRC so that we don't + // need to support it in the trainer. + if (frc && type_of((Move)sfen.move) == CASTLING) + { + continue; + } + // Return true if there is already enough data generated. const auto iter = counter.fetch_add(1); if (iter >= limit) @@ -435,7 +445,6 @@ namespace Stockfish::Tools else if (token == "set_recommended_uci_options") { UCI::setoption("Skill Level", "20"); - UCI::setoption("UCI_Chess960", "false"); UCI::setoption("UCI_LimitStrength", "false"); UCI::setoption("PruneAtShallowDepth", "false"); UCI::setoption("EnableTranspositionTable", "true"); diff --git a/src/tools/transform.cpp b/src/tools/transform.cpp index f657b410..0712658b 100644 --- a/src/tools/transform.cpp +++ b/src/tools/transform.cpp @@ -140,6 +140,7 @@ namespace Stockfish::Tools buffer.reserve(batch_size); + const bool frc = Options["UCI_Chess960"]; uint64_t num_processed = 0; for (;;) { @@ -149,7 +150,7 @@ namespace Stockfish::Tools auto& ps = v.value(); - pos.set_from_packed_sfen(ps.sfen, &si, th); + pos.set_from_packed_sfen(ps.sfen, &si, th, frc); auto static_eval = Eval::evaluate(pos); auto deep_eval = ps.score; ps.score = nudge(params, static_eval, deep_eval); @@ -290,6 +291,7 @@ namespace Stockfish::Tools Threads.execute_with_workers([&](auto& th){ Position& pos = th.rootPos; StateInfo si; + const bool frc = Options["UCI_Chess960"]; for(;;) { @@ -297,7 +299,7 @@ namespace Stockfish::Tools if (!fen.has_value()) return; - pos.set(*fen, false, &si, &th); + pos.set(*fen, frc, &si, &th); pos.state()->rule50 = 0; @@ -310,7 +312,7 @@ namespace Stockfish::Tools continue; PackedSfenValue ps; - pos.sfen_pack(ps.sfen); + pos.sfen_pack(ps.sfen, pos.is_chess960()); ps.score = search_value; ps.move = search_pv[0]; ps.gamePly = 1; @@ -401,6 +403,7 @@ namespace Stockfish::Tools Threads.execute_with_workers([&](auto& th){ Position& pos = th.rootPos; StateInfo si; + const bool frc = Options["UCI_Chess960"]; for (;;) { @@ -410,7 +413,7 @@ namespace Stockfish::Tools for(auto& ps : psv) { - pos.set_from_packed_sfen(ps.sfen, &si, &th); + pos.set_from_packed_sfen(ps.sfen, &si, &th, frc); for (int cnt = 0; cnt < params.research_count; ++cnt) Search::search(pos, params.depth, 1); @@ -420,7 +423,7 @@ namespace Stockfish::Tools if (search_pv.empty()) continue; - pos.sfen_pack(ps.sfen); + pos.sfen_pack(ps.sfen, false); ps.score = search_value; if (!params.keep_moves) ps.move = search_pv[0];