+template<typename... Ts>
+overload(Ts...) -> overload<Ts...>;
+
+void UCIEngine::print_info_string(const std::string& str) {
+ sync_cout_start();
+ for (auto& line : split(str, "\n"))
+ {
+ if (!is_whitespace(line))
+ {
+ std::cout << "info string " << line << '\n';
+ }
+ }
+ sync_cout_end();
+}
+
+UCIEngine::UCIEngine(int argc, char** argv) :
+ engine(argv[0]),
+ cli(argc, argv) {
+
+ engine.get_options().add_info_listener([](const std::optional<std::string>& str) {
+ if (str.has_value())
+ print_info_string(*str);
+ });
+
+ engine.set_on_iter([](const auto& i) { on_iter(i); });
+ engine.set_on_update_no_moves([](const auto& i) { on_update_no_moves(i); });
+ engine.set_on_update_full(
+ [this](const auto& i) { on_update_full(i, engine.get_options()["UCI_ShowWDL"]); });
+ engine.set_on_bestmove([](const auto& bm, const auto& p) { on_bestmove(bm, p); });
+}
+
+void UCIEngine::loop() {
+ std::string token, cmd;
+
+ for (int i = 1; i < cli.argc; ++i)
+ cmd += std::string(cli.argv[i]) + " ";
+
+ do
+ {
+ if (cli.argc == 1
+ && !getline(std::cin, cmd)) // Wait for an input or an end-of-file (EOF) indication
+ cmd = "quit";
+
+ std::istringstream is(cmd);
+
+ token.clear(); // Avoid a stale if getline() returns nothing or a blank line
+ is >> std::skipws >> token;
+
+ if (token == "quit" || token == "stop")
+ engine.stop();
+
+ // The GUI sends 'ponderhit' to tell that the user has played the expected move.
+ // So, 'ponderhit' is sent if pondering was done on the same move that the user
+ // has played. The search should continue, but should also switch from pondering
+ // to the normal search.
+ else if (token == "ponderhit")
+ engine.set_ponderhit(false);
+
+ else if (token == "uci")
+ {
+ sync_cout << "id name " << engine_info(true) << "\n"
+ << engine.get_options() << sync_endl;
+
+ sync_cout << "uciok" << sync_endl;
+ }
+
+ else if (token == "setoption")
+ setoption(is);
+ else if (token == "go")
+ {
+ // send info strings after the go command is sent for old GUIs and python-chess
+ print_info_string(engine.numa_config_information_as_string());
+ print_info_string(engine.thread_binding_information_as_string());
+ go(is);
+ }
+ else if (token == "position")
+ position(is);
+ else if (token == "ucinewgame")
+ engine.search_clear();
+ else if (token == "isready")
+ sync_cout << "readyok" << sync_endl;
+
+ // Add custom non-UCI commands, mainly for debugging purposes.
+ // These commands must not be used during a search!
+ else if (token == "flip")
+ engine.flip();
+ else if (token == "bench")
+ bench(is);
+ else if (token == "d")
+ sync_cout << engine.visualize() << sync_endl;
+ else if (token == "eval")
+ engine.trace_eval();
+ else if (token == "compiler")
+ sync_cout << compiler_info() << sync_endl;
+ else if (token == "export_net")
+ {
+ std::pair<std::optional<std::string>, std::string> files[2];
+
+ if (is >> std::skipws >> files[0].second)
+ files[0].first = files[0].second;
+
+ if (is >> std::skipws >> files[1].second)
+ files[1].first = files[1].second;
+
+ engine.save_network(files);
+ }
+ else if (token == "--help" || token == "help" || token == "--license" || token == "license")
+ sync_cout
+ << "\nStockfish is a powerful chess engine for playing and analyzing."
+ "\nIt is released as free software licensed under the GNU GPLv3 License."
+ "\nStockfish is normally used with a graphical user interface (GUI) and implements"
+ "\nthe Universal Chess Interface (UCI) protocol to communicate with a GUI, an API, etc."
+ "\nFor any further information, visit https://github.com/official-stockfish/Stockfish#readme"
+ "\nor read the corresponding README.md and Copying.txt files distributed along with this program.\n"
+ << sync_endl;
+ else if (!token.empty() && token[0] != '#')
+ sync_cout << "Unknown command: '" << cmd << "'. Type help for more information."
+ << sync_endl;
+
+ } while (token != "quit" && cli.argc == 1); // The command-line arguments are one-shot
+}
+
+Search::LimitsType UCIEngine::parse_limits(std::istream& is) {
+ Search::LimitsType limits;
+ std::string token;
+
+ limits.startTime = now(); // The search starts as early as possible
+
+ while (is >> token)
+ if (token == "searchmoves") // Needs to be the last command on the line
+ while (is >> token)
+ limits.searchmoves.push_back(to_lower(token));
+
+ else if (token == "wtime")
+ is >> limits.time[WHITE];
+ else if (token == "btime")
+ is >> limits.time[BLACK];
+ else if (token == "winc")
+ is >> limits.inc[WHITE];
+ else if (token == "binc")
+ is >> limits.inc[BLACK];
+ else if (token == "movestogo")
+ is >> limits.movestogo;
+ else if (token == "depth")
+ is >> limits.depth;
+ else if (token == "nodes")
+ is >> limits.nodes;
+ else if (token == "movetime")
+ is >> limits.movetime;
+ else if (token == "mate")
+ is >> limits.mate;
+ else if (token == "perft")
+ is >> limits.perft;
+ else if (token == "infinite")
+ limits.infinite = 1;
+ else if (token == "ponder")
+ limits.ponderMode = true;
+
+ return limits;
+}
+
+void UCIEngine::go(std::istringstream& is) {
+
+ Search::LimitsType limits = parse_limits(is);
+
+ if (limits.perft)
+ perft(limits);
+ else
+ engine.go(limits);
+}
+
+void UCIEngine::bench(std::istream& args) {
+ std::string token;
+ uint64_t num, nodes = 0, cnt = 1;
+ uint64_t nodesSearched = 0;
+ const auto& options = engine.get_options();
+
+ engine.set_on_update_full([&](const auto& i) {
+ nodesSearched = i.nodes;
+ on_update_full(i, options["UCI_ShowWDL"]);
+ });
+
+ std::vector<std::string> list = Benchmark::setup_bench(engine.fen(), args);
+
+ num = count_if(list.begin(), list.end(),
+ [](const std::string& s) { return s.find("go ") == 0 || s.find("eval") == 0; });
+
+ TimePoint elapsed = now();
+
+ for (const auto& cmd : list)
+ {
+ std::istringstream is(cmd);
+ is >> std::skipws >> token;
+
+ if (token == "go" || token == "eval")
+ {
+ std::cerr << "\nPosition: " << cnt++ << '/' << num << " (" << engine.fen() << ")"
+ << std::endl;
+ if (token == "go")
+ {
+ Search::LimitsType limits = parse_limits(is);
+
+ if (limits.perft)
+ nodesSearched = perft(limits);
+ else
+ {
+ engine.go(limits);
+ engine.wait_for_search_finished();
+ }
+
+ nodes += nodesSearched;
+ nodesSearched = 0;
+ }
+ else
+ engine.trace_eval();
+ }
+ else if (token == "setoption")
+ setoption(is);
+ else if (token == "position")
+ position(is);
+ else if (token == "ucinewgame")
+ {
+ engine.search_clear(); // search_clear may take a while
+ elapsed = now();
+ }
+ }