diff --git a/src/learn/gensfen.cpp b/src/learn/gensfen.cpp index afbcce37..f7cc5669 100644 --- a/src/learn/gensfen.cpp +++ b/src/learn/gensfen.cpp @@ -355,7 +355,8 @@ namespace Learner // It must be 2**N because it will be used as the mask to calculate hash_index. static_assert((GENSFEN_HASH_SIZE& (GENSFEN_HASH_SIZE - 1)) == 0); - MultiThinkGenSfen(int search_depth_min_, int search_depth_max_, SfenWriter& sw_) : + MultiThinkGenSfen(int search_depth_min_, int search_depth_max_, SfenWriter& sw_, const std::string& seed) : + MultiThink(seed), search_depth_min(search_depth_min_), search_depth_max(search_depth_max_), sfen_writer(sw_) @@ -1055,6 +1056,7 @@ namespace Learner bool random_file_name = false; std::string sfen_format; + std::string seed; while (true) { @@ -1111,6 +1113,8 @@ namespace Learner is >> detect_draw_by_insufficient_mating_material; else if (token == "sfen_format") is >> sfen_format; + else if (token == "seed") + is >> seed; else cout << "Error! : Illegal token " << token << endl; } @@ -1137,7 +1141,7 @@ namespace Learner { // Give a random number to output_file_name at this point. // Do not use std::random_device(). Because it always the same integers on MinGW. - PRNG r(std::chrono::system_clock::now().time_since_epoch().count()); + PRNG r(seed); // Just in case, reassign the random numbers. for (int i = 0; i < 10; ++i) r.rand(1); @@ -1182,7 +1186,7 @@ namespace Learner SfenWriter sfen_writer(output_file_name, thread_num); sfen_writer.set_save_interval(save_every); - MultiThinkGenSfen multi_think(search_depth_min, search_depth_max, sfen_writer); + MultiThinkGenSfen multi_think(search_depth_min, search_depth_max, sfen_writer, seed); multi_think.nodes = nodes; multi_think.set_loop_max(loop_max); multi_think.eval_limit = eval_limit; diff --git a/src/learn/learn.cpp b/src/learn/learn.cpp index 70459963..6d0a777d 100644 --- a/src/learn/learn.cpp +++ b/src/learn/learn.cpp @@ -432,8 +432,8 @@ namespace Learner // Do not use std::random_device(). // Because it always the same integers on MinGW. - SfenReader(int thread_num) : - prng(std::chrono::system_clock::now().time_since_epoch().count()) + SfenReader(int thread_num, const std::string& seed) : + prng(seed) { packed_sfens.resize(thread_num); total_read = 0; @@ -742,7 +742,8 @@ namespace Learner // Class to generate sfen with multiple threads struct LearnerThink : public MultiThink { - LearnerThink(SfenReader& sr_) : + LearnerThink(SfenReader& sr_, const std::string& seed) : + MultiThink(seed), sr(sr_), stop_flag(false), save_only_once(false) @@ -1437,7 +1438,7 @@ namespace Learner // Subcontracting the teacher shuffle "learn shuffle" command. // output_file_name: name of the output file where the shuffled teacher positions will be written - void shuffle_files(const vector& filenames, const string& output_file_name, uint64_t buffer_size) + void shuffle_files(const vector& filenames, const string& output_file_name, uint64_t buffer_size, const std::string& seed) { // The destination folder is // tmp/ for temporary writing @@ -1460,7 +1461,7 @@ namespace Learner // random number to shuffle // Do not use std::random_device(). Because it always the same integers on MinGW. - PRNG prng(std::chrono::system_clock::now().time_since_epoch().count()); + PRNG prng(seed); // generate the name of the temporary file auto make_filename = [](uint64_t i) @@ -1533,11 +1534,11 @@ namespace Learner // Subcontracting the teacher shuffle "learn shuffleq" command. // This is written in 1 pass. // output_file_name: name of the output file where the shuffled teacher positions will be written - void shuffle_files_quick(const vector& filenames, const string& output_file_name) + void shuffle_files_quick(const vector& filenames, const string& output_file_name, const std::string& seed) { // random number to shuffle // Do not use std::random_device(). Because it always the same integers on MinGW. - PRNG prng(std::chrono::system_clock::now().time_since_epoch().count()); + PRNG prng(seed); // number of files const size_t file_count = filenames.size(); @@ -1573,7 +1574,7 @@ namespace Learner // Subcontracting the teacher shuffle "learn shufflem" command. // Read the whole memory and write it out with the specified file name. - void shuffle_files_on_memory(const vector& filenames, const string output_file_name) + void shuffle_files_on_memory(const vector& filenames, const string output_file_name, const std::string& seed) { PSVector buf; @@ -1591,7 +1592,7 @@ namespace Learner // shuffle from buf[0] to buf[size-1] // Do not use std::random_device(). Because it always the same integers on MinGW. - PRNG prng(std::chrono::system_clock::now().time_since_epoch().count()); + PRNG prng(seed); uint64_t size = (uint64_t)buf.size(); std::cout << "shuffle buf.size() = " << size << std::endl; @@ -1613,9 +1614,7 @@ namespace Learner void learn(Position&, istringstream& is) { const auto thread_num = (int)Options["Threads"]; - SfenReader sr(thread_num); - LearnerThink learn_think(sr); vector filenames; // mini_batch_size 1M aspect by default. This can be increased. @@ -1704,6 +1703,7 @@ namespace Learner uint64_t mirror_percentage = 0; string validation_set_file_name; + string seed; // Assume the filenames are staggered. while (true) @@ -1811,7 +1811,7 @@ namespace Learner else if (option == "dest_score_min_value") is >> dest_score_min_value; else if (option == "dest_score_max_value") is >> dest_score_max_value; else if (option == "convert_teacher_signal_to_winning_probability") is >> convert_teacher_signal_to_winning_probability; - + else if (option == "seed") is >> seed; // Otherwise, it's a filename. else filenames.push_back(option); @@ -1829,6 +1829,9 @@ namespace Learner cout << "Warning! OpenMP disabled." << endl; #endif + SfenReader sr(thread_num, seed); + LearnerThink learn_think(sr, seed); + // Display learning game file if (target_dir != "") { @@ -1861,21 +1864,21 @@ namespace Learner { cout << "buffer_size : " << buffer_size << endl; cout << "shuffle mode.." << endl; - shuffle_files(filenames, output_file_name, buffer_size); + shuffle_files(filenames, output_file_name, buffer_size, seed); return; } if (shuffle_quick) { cout << "quick shuffle mode.." << endl; - shuffle_files_quick(filenames, output_file_name); + shuffle_files_quick(filenames, output_file_name, seed); return; } if (shuffle_on_memory) { cout << "shuffle on memory.." << endl; - shuffle_files_on_memory(filenames, output_file_name); + shuffle_files_on_memory(filenames, output_file_name, seed); return; } diff --git a/src/learn/multi_think.h b/src/learn/multi_think.h index 7de9d6b9..4b5662aa 100644 --- a/src/learn/multi_think.h +++ b/src/learn/multi_think.h @@ -10,6 +10,8 @@ #include #include #include +#include +#include // Learning from a game record, when making yourself think and generating a fixed track, etc. @@ -19,10 +21,11 @@ struct MultiThink { static constexpr std::uint64_t LOOP_COUNT_FINISHED = std::numeric_limits::max(); - MultiThink() : prng(std::chrono::system_clock::now().time_since_epoch().count()) - { - loop_count = 0; - } + MultiThink() : prng{}, loop_count(0) { } + + MultiThink(std::uint64_t seed) : prng(seed), loop_count(0) { } + + MultiThink(const std::string& seed) : prng(seed), loop_count(0) { } // Call this function from the master thread, each thread will think, // Return control when the thought ending condition is satisfied.