From 5c57b860c99eb88d669ed9a78583fa0c88574e1a Mon Sep 17 00:00:00 2001 From: Drew Lewis Date: Tue, 11 Jun 2024 17:46:23 +0000 Subject: [PATCH] Add a StepVerbose override to the UCIBot PiperOrigin-RevId: 642319505 Change-Id: I16c1ea4d79a3ccba94716a08381a5d745e3176a6 --- open_spiel/bots/uci/random_uci_bot.cc | 5 +++++ open_spiel/bots/uci/uci_bot.cc | 29 +++++++++++++++++++-------- open_spiel/bots/uci/uci_bot.h | 9 +++++++-- open_spiel/bots/uci/uci_bot_test.cc | 17 +++++++++++++++- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/open_spiel/bots/uci/random_uci_bot.cc b/open_spiel/bots/uci/random_uci_bot.cc index 5e49e4ff03..7ad7dd0ac7 100644 --- a/open_spiel/bots/uci/random_uci_bot.cc +++ b/open_spiel/bots/uci/random_uci_bot.cc @@ -79,10 +79,15 @@ void RandomUciBot() { absl::StartsWith(line, "go depth") || absl::StartsWith(line, "go nodes") || absl::StartsWith(line, "go mate")) { + std::cout << "info string Random uci bot uci info statistics may not be " + "accurate.\n"; std::vector legal_actions = state->LegalActions(); int index = absl::Uniform(rng, 0, legal_actions.size()); Action action = legal_actions[index]; chess::Move move = ActionToMove(action, chess_state->Board()); + std::cout << "info depth 1 seldepth 1 multipv 1 nodes 1 nps 1000 " + "hashfull 0 tbhits 0 time 1 pv " + << move.ToLAN() << "\n"; std::cout << "bestmove " << move.ToLAN() << std::endl; } else if (line == "quit") { return; diff --git a/open_spiel/bots/uci/uci_bot.cc b/open_spiel/bots/uci/uci_bot.cc index be39a893fb..7cae3626d9 100644 --- a/open_spiel/bots/uci/uci_bot.cc +++ b/open_spiel/bots/uci/uci_bot.cc @@ -91,20 +91,23 @@ UCIBot::~UCIBot() { close(output_fd_); } -Action UCIBot::Step(const State& state) { +Action UCIBot::Step(const State& state) { return StepVerbose(state).first; } + +std::pair UCIBot::StepVerbose(const State& state) { std::string move_str; + std::string info_str; // Contains the last info string from the bot. auto chess_state = down_cast(state); if (ponder_ && ponder_move_) { if (!was_ponder_hit_) { Stop(); Position(chess_state.Board().ToFEN()); - tie(move_str, ponder_move_) = Go(); + tie(move_str, ponder_move_) = Go(&info_str); } else { - tie(move_str, ponder_move_) = ReadBestMove(); + tie(move_str, ponder_move_) = ReadBestMove(&info_str); } } else { Position(chess_state.Board().ToFEN()); - tie(move_str, ponder_move_) = Go(); + tie(move_str, ponder_move_) = Go(&info_str); } was_ponder_hit_ = false; auto move = chess_state.Board().ParseLANMove(move_str); @@ -118,7 +121,7 @@ Action UCIBot::Step(const State& state) { } Action action = chess::MoveToAction(*move); - return action; + return {action, info_str}; } void UCIBot::Restart() { @@ -239,9 +242,10 @@ void UCIBot::Position(const std::string& fen, Write(msg); } -std::pair> UCIBot::Go() { +std::pair> UCIBot::Go( + absl::optional info_string) { Write("go " + search_limit_string_); - return ReadBestMove(); + return ReadBestMove(info_string); } void UCIBot::GoPonder() { Write("go ponder " + search_limit_string_); } @@ -255,10 +259,19 @@ std::pair> UCIBot::Stop() { void UCIBot::Quit() { Write("quit"); } -std::pair> UCIBot::ReadBestMove() { +std::pair> UCIBot::ReadBestMove( + absl::optional info_string) { while (true) { // istringstream can't use a string_view so we need to copy to a string. std::string response = ReadLine(); + // Save the most recent info string if requested. Specifying that the string + // contains the number of nodes makes sure that we don't save strings of the + // form "info depth 30 currmove c2c1 currmovenumber 22", we want the ones + // with metadata about the search. + if (info_string.has_value() && absl::StartsWith(response, "info") && + absl::StrContains(response, "nodes")) { + *info_string.value() = response; + } std::istringstream response_line(response); std::string token; std::string move_str; diff --git a/open_spiel/bots/uci/uci_bot.h b/open_spiel/bots/uci/uci_bot.h index 01784c2480..ba01e09aef 100644 --- a/open_spiel/bots/uci/uci_bot.h +++ b/open_spiel/bots/uci/uci_bot.h @@ -53,6 +53,9 @@ class UCIBot : public Bot { ~UCIBot() override; Action Step(const State& state) override; + + std::pair StepVerbose(const State& state) override; + void Restart() override; void RestartAt(const State& state) override; @@ -72,12 +75,14 @@ class UCIBot : public Bot { void SetOption(const std::string& name, const std::string& value); void UciNewGame(); void IsReady(); - std::pair> Go(); + std::pair> Go( + absl::optional info_string = absl::nullopt); void GoPonder(); void PonderHit(); std::pair> Stop(); void Quit(); - std::pair> ReadBestMove(); + std::pair> ReadBestMove( + absl::optional info_string = absl::nullopt); pid_t pid_ = -1; int output_fd_ = -1; diff --git a/open_spiel/bots/uci/uci_bot_test.cc b/open_spiel/bots/uci/uci_bot_test.cc index cb93233103..6e011dc3c0 100644 --- a/open_spiel/bots/uci/uci_bot_test.cc +++ b/open_spiel/bots/uci/uci_bot_test.cc @@ -21,9 +21,11 @@ #include "open_spiel/abseil-cpp/absl/flags/flag.h" #include "open_spiel/abseil-cpp/absl/flags/parse.h" +#include "open_spiel/abseil-cpp/absl/strings/match.h" #include "open_spiel/algorithms/evaluate_bots.h" #include "open_spiel/spiel.h" #include "open_spiel/spiel_bots.h" +#include "open_spiel/spiel_utils.h" #include "open_spiel/utils/init.h" ABSL_FLAG(std::string, binary, "random_uci_bot", "Name of the binary to run."); @@ -43,7 +45,7 @@ void RandomUciBotTest() { /*ponder*/ false, /*options*/ options); auto bot2 = std::make_unique(binary, /*move_time*/ 10, /*ponder*/ false, /*options*/ options); - std::vector bots = {bot1.get(), bot2.get()}; + std::vector bots = {bot1.get(), bot2.get()}; for (int i = 0; i < kNumGames; ++i) { std::unique_ptr state = game->NewInitialState(); EvaluateBots(state.get(), bots, kSeed); @@ -51,6 +53,18 @@ void RandomUciBotTest() { } } +void CheckVerboseOutput() { + std::string binary = absl::GetFlag(FLAGS_binary); + std::shared_ptr game = LoadGame("chess"); + auto bot = UCIBot(binary, /*move_time*/ 10, + /*ponder*/ false, /*options*/ {}); + std::unique_ptr state = game->NewInitialState(); + auto [action, info] = bot.StepVerbose(*state); + + SPIEL_CHECK_TRUE(absl::StrContains(info, "info")); + std::cout << "Verbose output: " << info << std::endl; +} + } // namespace } // namespace uci } // namespace open_spiel @@ -58,5 +72,6 @@ void RandomUciBotTest() { int main(int argc, char **argv) { open_spiel::Init("", &argc, &argv, false); absl::ParseCommandLine(argc, argv); + open_spiel::uci::CheckVerboseOutput(); open_spiel::uci::RandomUciBotTest(); }