diff --git a/ben-bot/include/ben-bot/Engine.hpp b/ben-bot/include/ben-bot/Engine.hpp index ef752b2a..dd777570 100644 --- a/ben-bot/include/ben-bot/Engine.hpp +++ b/ben-bot/include/ben-bot/Engine.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ namespace ben_bot { using chess::game::Position; +using chess::moves::Move; using std::string_view; namespace uci = chess::uci; @@ -83,6 +85,8 @@ class [[nodiscard]] Engine final : public uci::EngineBase { void set_pretty_printing(bool shouldPrettyPrint); + [[nodiscard]] auto pretty_print_move(Move move) const -> std::string; + static void print_compiler_info(); static void start_file_logger(string_view path); @@ -99,7 +103,7 @@ class [[nodiscard]] Engine final : public uci::EngineBase { uci::IntOption ttSize { "Hash", 1, 2048, 16, - "Sets the maximum transposition table size (in MB)", + "Sets the maximum transposition table size (in MB).", [this](const int sizeMB) { wait(); searcher.context.transTable.resize( @@ -110,7 +114,7 @@ class [[nodiscard]] Engine final : public uci::EngineBase { uci::Action clearTT { "Clear Hash", [this] { searcher.context.clear_transposition_table(); }, - "Press to clear the transposition table" + "Press to clear the transposition table." }; // The engine doesn't start pondering on its own without explicitly being told to @@ -120,12 +124,12 @@ class [[nodiscard]] Engine final : public uci::EngineBase { uci::BoolOption ponder { "Ponder", true, - "Controls whether pondering is allowed" + "Controls whether pondering is allowed." }; uci::IntOption threads { "Threads", 1, 1, 1, - "Number of searcher threads (currently a dummy)" + "Number of searcher threads (currently a dummy)." }; uci::IntOption moveOverhead { @@ -135,19 +139,26 @@ class [[nodiscard]] Engine final : public uci::EngineBase { uci::StringOption logFile { "Debug Log File", "", - "If not empty, engine I/O will be mirrored to this file", + "If not empty, engine I/O will be mirrored to this file.", start_file_logger }; uci::BoolOption prettyPrintMode { "Pretty Print", PRETTY_PRINT_DEFAULT, - "When on, search output is pretty-printed instead of printed in UCI format", + "When on, search output is pretty-printed instead of printed in UCI format.", [this](const bool usePretty) { set_pretty_printing(usePretty); } }; - std::array options { - &ttSize, &clearTT, &ponder, &threads, &moveOverhead, &logFile, &prettyPrintMode + uci::ComboOption moveFormat { + "Move Format", + { "UCI", "Algebraic", "ICCF" }, + "Algebraic", + "Notation format used to display moves in pretty printing mode." + }; + + std::array options { + &ttSize, &clearTT, &ponder, &threads, &moveOverhead, &logFile, &prettyPrintMode, &moveFormat }; /* ----- Custom commands ----- */ diff --git a/ben-bot/src/Engine.cpp b/ben-bot/src/Engine.cpp index cf222b40..faf42476 100644 --- a/ben-bot/src/Engine.cpp +++ b/ben-bot/src/Engine.cpp @@ -41,7 +41,8 @@ void Engine::new_game(const bool firstCall) // initializing them in the constructor to avoid referencing the // `this` pointer in the constructor if (prettyPrinting.load()) { - searcher.context.callbacks = search::Callbacks::make_pretty_printer(); + searcher.context.callbacks = search::Callbacks::make_pretty_printer( + [this](const Move move) { return pretty_print_move(move); }); } else { searcher.context.callbacks = search::Callbacks::make_uci_printer( [this] noexcept { // cppcheck-suppress syntaxError @@ -59,7 +60,8 @@ void Engine::set_pretty_printing(const bool shouldPrettyPrint) wait(); if (shouldPrettyPrint) { - searcher.context.callbacks = search::Callbacks::make_pretty_printer(); + searcher.context.callbacks = search::Callbacks::make_pretty_printer( + [this](const Move move) { return pretty_print_move(move); }); } else { searcher.context.callbacks = search::Callbacks::make_uci_printer( [this] noexcept { return debugMode.load(memory_order_relaxed); }); diff --git a/ben-bot/src/Perft.cpp b/ben-bot/src/Perft.cpp index a03e53c2..634da295 100644 --- a/ben-bot/src/Perft.cpp +++ b/ben-bot/src/Perft.cpp @@ -14,28 +14,31 @@ #include #include +#include +#include #include -#include #include #include #include +#include namespace ben_bot { -namespace util = chess::util; -namespace notation = chess::notation; +namespace util = chess::util; using uci::printing::info_string; namespace { using chess::moves::PerftResult; - void print_root_nodes(const PerftResult& result) + void print_root_nodes( + const PerftResult& result, + const std::function& printMove) { for (const auto& [move, numChildren] : result.rootNodes) { info_string(std::format( "Move {}: {} child nodes", - notation::to_uci(move), numChildren)); + printMove(move), numChildren)); } } @@ -66,7 +69,10 @@ void Engine::run_perft(const string_view arguments) const depth, searcher.context.options.position); std::println(""); - print_root_nodes(result); + + print_root_nodes( + result, + [this](const Move move) { return pretty_print_move(move); }); std::println(""); print_results(result); diff --git a/ben-bot/src/Printing.cpp b/ben-bot/src/Printing.cpp index 37ba8c37..507655b9 100644 --- a/ben-bot/src/Printing.cpp +++ b/ben-bot/src/Printing.cpp @@ -20,7 +20,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -29,6 +31,7 @@ #include #include #include +#include #include namespace ben_bot { @@ -151,7 +154,7 @@ void Engine::print_current_position(const string_view arguments) const searcher.context.transTable .find(pos) - .transform([](const TTData& data) { + .transform([this](const TTData& data) { print_labeled_info( "TT hit: ", std::format( @@ -159,7 +162,7 @@ void Engine::print_current_position(const string_view arguments) const data.searchedDepth, data.eval, magic_enum::enum_name(data.evalType), eval::Score::from_tt(data.eval, 0uz), - chess::notation::to_uci(data.bestMove.value_or(Move { })))); + pretty_print_move(data.bestMove.value_or(Move { })))); return std::monostate { }; }); @@ -182,4 +185,22 @@ void Engine::print_compiler_info() resources::get_build_time())); } +auto Engine::pretty_print_move(const Move move) const -> std::string +{ + const auto format = moveFormat.get_value(); + + if (format == "UCI") + return chess::notation::to_uci(move); + + if (format == "Algebraic") + return chess::notation::to_alg( + searcher.context.options.position, move); + + if (format == "ICCF") + return chess::notation::to_iccf(move); + + std::unreachable(); + return { }; +} + } // namespace ben_bot diff --git a/libbenbot/include/libbenbot/search/Callbacks.hpp b/libbenbot/include/libbenbot/search/Callbacks.hpp index f52f4208..bd92850e 100644 --- a/libbenbot/include/libbenbot/search/Callbacks.hpp +++ b/libbenbot/include/libbenbot/search/Callbacks.hpp @@ -20,6 +20,11 @@ #pragma once #include +#include + +namespace chess::moves { +struct Move; +} namespace ben_bot::search { @@ -86,9 +91,13 @@ struct Callbacks final { /** Creates a set of callbacks that print search information in a human-readable table-aligned format. + @param printMove Function object that will be used to format Move objects to display + the PV. + @note The output produced by these callbacks does not conform to the UCI protocol! */ - [[nodiscard]] static auto make_pretty_printer() + [[nodiscard]] static auto make_pretty_printer( + std::function&& printMove) -> Callbacks; }; diff --git a/libbenbot/src/search/Callbacks.cpp b/libbenbot/src/search/Callbacks.cpp index 148b5d8a..847abd34 100644 --- a/libbenbot/src/search/Callbacks.cpp +++ b/libbenbot/src/search/Callbacks.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -242,8 +241,11 @@ namespace { std::cout << termcolor::reset; } + using MovePrinter = std::function; + [[nodiscard]] auto format_pv( - const std::span pv) -> string + const std::span pv, + const MovePrinter& printMove) -> string { if (pv.empty()) { // this is possible if we're checkmated @@ -253,7 +255,7 @@ namespace { string result; for (const auto move : pv) { - result.append(chess::notation::to_uci(move)); + result.append(printMove(move)); result.append(1uz, ' '); } @@ -282,7 +284,8 @@ namespace { << termcolor::reset; } - void pretty_print(const Result& res) + void pretty_print( + const Result& res, const MovePrinter& printMove) { // depth print_column_text( @@ -310,19 +313,25 @@ namespace { print_score(libchess.score); // PV - std::cout << format_pv(res.pv) << '\n'; + std::cout << format_pv(res.pv, printMove) + << '\n'; } } // namespace -auto Callbacks::make_pretty_printer() +auto Callbacks::make_pretty_printer( + MovePrinter&& printMove) -> Callbacks { + auto printIteration = [formatMove = std::move(printMove)](const Result& res) { + pretty_print(res, formatMove); + }; + return { .onSearchStart = []([[maybe_unused]] const Options& options) { print_table_header(); }, - .onSearchComplete = pretty_print, - .onIteration = pretty_print + .onSearchComplete = printIteration, + .onIteration = printIteration }; }