Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d7d999e
style: no longer using info_string() to print current position info
benthevining Oct 16, 2025
8ae96f4
feat: initial commit of termcolor dependency (#385)
benthevining Oct 16, 2025
14f51c2
feat: position printing now uses some color (#385)
benthevining Oct 16, 2025
5f676f8
feat: printing text tables using color (#385)
benthevining Oct 16, 2025
925f125
feat: printing logo in color (#385)
benthevining Oct 16, 2025
385ea56
fix: better text colors
benthevining Oct 16, 2025
acffbb2
Merge branch 'main' into pretty-printing
benthevining Oct 17, 2025
39b7254
Merge branch 'main' into pretty-printing
benthevining Oct 17, 2025
c1777d7
refactor: moved colored printing functions to separate TU
benthevining Oct 17, 2025
02b9c60
refactor: get_at_most_n_lines() utility function
benthevining Oct 19, 2025
af34f86
Merge branch 'main' into pretty-printing
benthevining Oct 19, 2025
e5c17a4
chore: merging upstream
benthevining Oct 28, 2025
d7d41e1
test: test case to verify TextTable::to_string() and TextTable::print()
benthevining Oct 28, 2025
b1e36fe
Merge branch 'main' into pretty-printing
benthevining Nov 3, 2025
8d35972
chore: updating precommit hooks
benthevining Feb 27, 2026
b3d5757
chore: merging upstream
benthevining Feb 27, 2026
7511881
chore: formatting
benthevining Feb 27, 2026
2350573
Merge branch 'main' into pretty-printing
benthevining Feb 27, 2026
bfcb567
refactor: misc
benthevining Feb 27, 2026
0f713f1
feat: UCI option to switch between pretty/UCI printing
benthevining Feb 27, 2026
da60d9a
feat: initial commit of pretty printing depth & time
benthevining Feb 27, 2026
5ab8c31
feat: pretty printing number of nodes
benthevining Feb 27, 2026
7913f41
feat: pretty printing NPS
benthevining Feb 27, 2026
0eaf8f2
feat: pretty printing hashfull
benthevining Feb 27, 2026
78c7df9
feat: pretty printing score
benthevining Feb 27, 2026
a12b241
feat: pretty printing PV
benthevining Feb 27, 2026
f8df078
feat: printing table header
benthevining Feb 27, 2026
f27e5e5
refactor: deduplicating code between UCI & pretty printers
benthevining Feb 27, 2026
2be8e23
feat: color printing in pretty output
benthevining Feb 27, 2026
f410950
refactor: misc
benthevining Feb 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ repos:

# C++ formatting & linting
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v21.1.8
rev: v22.1.0
hooks:
- id: clang-format
exclude_types: [json]
Expand Down
19 changes: 19 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,25 @@ cpack_add_component_group (
ben_bot_all DISPLAY_NAME "BenBot" DESCRIPTION "All components of BenBot" EXPANDED BOLD_TITLE
)

FetchContent_Declare (
termcolor
SYSTEM
GIT_REPOSITORY "https://github.com/ikalnytskyi/termcolor.git"
GIT_TAG v2.1.0
GIT_SHALLOW ON
SOURCE_SUBDIR foo # we need to disable adding termcolor as a subdirectory, because they list a
# too-old cmake version
FIND_PACKAGE_ARGS 2.1.0
)

FetchContent_MakeAvailable (termcolor)

if (termcolor_SOURCE_DIR)
set (TERMCOLOR_INCLUDES "${termcolor_SOURCE_DIR}/include")
elseif (termcolor_DIR)
set (TERMCOLOR_INCLUDES "${termcolor_DIR}/include")
endif ()

add_subdirectory (libchess)
add_subdirectory (libbenbot)
add_subdirectory (ben-bot)
Expand Down
31 changes: 18 additions & 13 deletions ben-bot/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,32 @@

add_subdirectory (resources)

set (benbot_sources
# cmake-format: sortable
src/Bench.cpp src/CLI.cpp src/Engine.cpp src/main.cpp src/Perft.cpp src/Printing.cpp
)

add_executable (ben_bot ${benbot_sources})
add_executable (ben_bot)

target_compile_features (ben_bot PRIVATE cxx_std_23)

target_link_libraries (ben_bot PRIVATE ben_bot::libbenbot ben_bot::resources)

set_property (
SOURCE src/ColorPrinting.cpp APPEND PROPERTY INCLUDE_DIRECTORIES "${TERMCOLOR_INCLUDES}"
)

set_target_properties (
ben_bot PROPERTIES CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF DEBUG_POSTFIX -d
)

set (benbot_headers
# cmake-format: sortable
include/ben-bot/CLI.hpp include/ben-bot/CustomCommand.hpp include/ben-bot/Engine.hpp
file (GLOB_RECURSE benbot_headers LIST_DIRECTORIES false CONFIGURE_DEPENDS
"${CMAKE_CURRENT_LIST_DIR}/include/*.hpp"
)

target_sources (ben_bot PRIVATE FILE_SET HEADERS BASE_DIRS include FILES ${benbot_headers})
file (GLOB benbot_sources LIST_DIRECTORIES false CONFIGURE_DEPENDS
"${CMAKE_CURRENT_LIST_DIR}/src/*.cpp"
)

target_sources (
ben_bot PRIVATE FILE_SET HEADERS BASE_DIRS include FILES ${benbot_headers}
PRIVATE ${benbot_sources}
)

source_group (TREE "${CMAKE_CURRENT_LIST_DIR}" FILES ${benbot_headers} ${benbot_sources})

Expand All @@ -50,9 +55,9 @@ set (
"Apple developer ID string, for example 'Developer ID Application: Your Name (2DO8NL92GO)'"
)

add_feature_info (
benbot_codesigning "CODESIGN_PROGRAM AND CODESIGN_ID" "Signing of BenBot binaries"
)
include (FeatureSummary)

add_feature_info (codesigning "CODESIGN_PROGRAM AND CODESIGN_ID" "Signing of BenBot binaries")

if (CODESIGN_PROGRAM AND CODESIGN_ID)
add_custom_command (
Expand Down
55 changes: 55 additions & 0 deletions ben-bot/include/ben-bot/ColorPrinting.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* ======================================================================================
*
* ░▒▓███████▓▒░░▒▓████████▓▒░▒▓███████▓▒░ ░▒▓███████▓▒░ ░▒▓██████▓▒░▒▓████████▓▒░
* ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
* ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
* ░▒▓███████▓▒░░▒▓██████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓███████▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
* ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
* ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
* ░▒▓███████▓▒░░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓███████▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░
*
* ======================================================================================
*/

/** @file
This file provides functions for colored printing.

@ingroup benbot
*/

#pragma once

#include <string_view>

namespace chess::util::strings {
struct TextTable;
} // namespace chess::util::strings

namespace chess::game {
struct Position;
} // namespace chess::game

namespace ben_bot {

using chess::game::Position;
using std::string_view;

/** @ingroup benbot
@{
*/

/** Prints the given table with bold headings, faint outlines,
and regular content cells.
*/
void print_colored_table(const chess::util::strings::TextTable& table);

/** Prints the given position with faint file/rank labels. */
void print_colored_board(const Position& pos, bool utf8);

/** Prints the given label faintly, and the info as regular text. */
void print_labeled_info(string_view label, string_view info);

/** @} */

} // namespace ben_bot
22 changes: 20 additions & 2 deletions ben-bot/include/ben-bot/Engine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,21 @@ class [[nodiscard]] Engine final : public uci::EngineBase {
void print_options(string_view args) const;
void print_current_position(string_view arguments) const;

void set_pretty_printing(bool shouldPrettyPrint);

static void print_compiler_info();

static void start_file_logger(string_view path);

static constexpr bool PRETTY_PRINT_DEFAULT = false;

std::atomic_bool debugMode { false };
std::atomic_bool prettyPrinting { PRETTY_PRINT_DEFAULT };

search::Thread searcher;

/* ----- UCI options ----- */

uci::IntOption ttSize {
"Hash",
1, 2048, 16,
Expand Down Expand Up @@ -129,10 +136,21 @@ class [[nodiscard]] Engine final : public uci::EngineBase {
uci::StringOption logFile {
"Debug Log File", "<empty>",
"If not empty, engine I/O will be mirrored to this file",
[](const string_view path) { start_file_logger(path); }
start_file_logger
};

uci::BoolOption prettyPrintMode {
"Pretty Print",
PRETTY_PRINT_DEFAULT,
"When on, search output is pretty-printed instead of printed in UCI format",
[this](const bool usePretty) { set_pretty_printing(usePretty); }
};

std::array<uci::Option*, 7uz> options {
&ttSize, &clearTT, &ponder, &threads, &moveOverhead, &logFile, &prettyPrintMode
};

std::array<uci::Option*, 6uz> options { &ttSize, &clearTT, &ponder, &threads, &moveOverhead, &logFile };
/* ----- Custom commands ----- */

// clang-format off
std::array<CustomCommand, 8uz> customCommands {
Expand Down
2 changes: 1 addition & 1 deletion ben-bot/resources/src/BuildTime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ namespace {

[[nodiscard, gnu::cold]] auto to_utc_time(const std::time_t time) -> std::tm
{
std::tm ret {};
std::tm ret { };

// this is written this way to avoid using the thread-unsafe function gmtime()
// gmtime_s() doesn't appear to be present on MacOS, and Windows's argument order
Expand Down
5 changes: 3 additions & 2 deletions ben-bot/src/Bench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ namespace {
// clang-format off
search::Thread thread {
search::Callbacks {
.onSearchStart = nullptr,
.onSearchComplete = [this](const SearchResult& res) { print_info(res); result = res; },
.onIteration = [this](const SearchResult& res) { print_info(res); }
}
Expand Down Expand Up @@ -161,13 +162,13 @@ namespace {
const auto totalNodes = std::transform_reduce(
results.begin(), results.end(),
0uz,
std::plus {},
std::plus { },
[](const SearchResult& result) { return result.nodesSearched; });

const auto totalTime = std::transform_reduce(
results.begin(), results.end(),
milliseconds { 0 },
std::plus {},
std::plus { },
[](const SearchResult& result) { return result.duration; });

const auto seconds = static_cast<double>(totalTime.count()) * 0.001;
Expand Down
109 changes: 109 additions & 0 deletions ben-bot/src/ColorPrinting.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* ======================================================================================
*
* ░▒▓███████▓▒░░▒▓████████▓▒░▒▓███████▓▒░ ░▒▓███████▓▒░ ░▒▓██████▓▒░▒▓████████▓▒░
* ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
* ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
* ░▒▓███████▓▒░░▒▓██████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓███████▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
* ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
* ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
* ░▒▓███████▓▒░░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓███████▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░
*
* ======================================================================================
*/

#include <beman/inplace_vector/inplace_vector.hpp>
#include <ben-bot/ColorPrinting.hpp>
#include <ben-bot/Engine.hpp>
#include <ben-bot/Resources.hpp>
#include <iostream>
#include <libchess/game/Position.hpp>
#include <libchess/util/Strings.hpp>
#include <libchess/util/TextTable.hpp>
#include <ranges>
#include <string_view>
#include <termcolor/termcolor.hpp>

namespace ben_bot {

using std::cout;
using std::string_view;

namespace {
template <size_t MaxLines>
[[nodiscard, gnu::const]] auto get_at_most_n_lines(const string_view input)
{
using Lines = beman::inplace_vector::inplace_vector<string_view, MaxLines>;

return chess::util::strings::lines_view(input)
| std::views::take(MaxLines)
| std::ranges::to<Lines>();
}
} // namespace

void Engine::print_logo_and_version() const
{
const auto logoLines = get_at_most_n_lines<11uz>(resources::get_ascii_logo());

cout << termcolor::grey << logoLines.front() << '\n'
<< termcolor::blue;

for (const auto line : logoLines | std::views::drop(1) | std::views::take(logoLines.capacity() - 2uz))
cout << line << '\n';

cout << termcolor::grey << logoLines.back() << "\n\n"
<< termcolor::reset << termcolor::bold << get_name() << ", "
<< termcolor::reset << "by " << get_author() << '\n'
<< termcolor::reset;
}

void print_colored_table(const chess::util::strings::TextTable& table)
{
table.print(
[](const string_view heading) {
// we want the heading text to be underlined, but not the
// whitespace that follows the text to complete the cell
const auto trimmed = chess::util::strings::trim(heading);

cout << termcolor::bold << termcolor::underline
<< trimmed
<< termcolor::reset;

const auto numSpaces = heading.length() - trimmed.length();

for (auto i = 0uz; i < numSpaces; ++i)
cout << ' ';
},
[](const string_view cell) {
cout << cell;
},
[](const string_view outline) {
cout << termcolor::white << outline << termcolor::reset;
},
[] { cout << '\n'; });

cout << termcolor::reset;
}

void print_colored_board(const Position& pos, const bool utf8)
{
const auto boardStr = utf8 ? print_utf8(pos) : print_ascii(pos);

const auto lines = get_at_most_n_lines<9uz>(boardStr);

for (const auto line : lines | std::views::take(lines.capacity() - 1uz)) {
cout << line.substr(0uz, line.length() - 1uz)
<< termcolor::white << line.back() << '\n'
<< termcolor::reset;
}

cout << termcolor::white << lines.back() << '\n'
<< termcolor::reset;
}

void print_labeled_info(const string_view label, const string_view info)
{
cout << termcolor::white << label << termcolor::reset << info << '\n';
}

} // namespace ben_bot
27 changes: 25 additions & 2 deletions ben-bot/src/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

namespace ben_bot {

using std::memory_order_relaxed;
using std::size_t;
using uci::printing::info_string;

Expand All @@ -39,8 +40,30 @@ void Engine::new_game(const bool firstCall)
// we use delayed initialization for these callbacks instead of
// initializing them in the constructor to avoid referencing the
// `this` pointer in the constructor
searcher.context.callbacks = search::Callbacks::make_uci_printer(
[this] { return debugMode.load(std::memory_order::relaxed); });
if (prettyPrinting.load()) {
searcher.context.callbacks = search::Callbacks::make_pretty_printer();
} else {
searcher.context.callbacks = search::Callbacks::make_uci_printer(
[this] noexcept { // cppcheck-suppress syntaxError
return debugMode.load(memory_order_relaxed);
});
}
}

void Engine::set_pretty_printing(const bool shouldPrettyPrint)
{
// check if the requested printing mode was already active
if (prettyPrinting.exchange(shouldPrettyPrint, memory_order_relaxed) == shouldPrettyPrint)
return;

wait();

if (shouldPrettyPrint) {
searcher.context.callbacks = search::Callbacks::make_pretty_printer();
} else {
searcher.context.callbacks = search::Callbacks::make_uci_printer(
[this] noexcept { return debugMode.load(memory_order_relaxed); });
}
}

void Engine::go(const uci::GoCommandOptions& opts)
Expand Down
Loading
Loading