Skip to content

Commit 7a3e63a

Browse files
committed
refactor: moved debugging state into engine base class, added UCI option for sanitize positions
1 parent 0ea0c4b commit 7a3e63a

File tree

6 files changed

+61
-37
lines changed

6 files changed

+61
-37
lines changed

libbenbot/include/libbenbot/engine/Engine.hpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,6 @@ class [[nodiscard]] Engine final : public uci::EngineBase {
8080

8181
auto is_searching() const noexcept -> bool override { return searcher.context.in_progress(); }
8282

83-
void set_debug(const bool shouldDebug) override
84-
{
85-
debugMode.store(shouldDebug, std::memory_order::relaxed);
86-
}
87-
8883
[[nodiscard]] auto get_options() -> std::span<uci::Option*> override { return options; }
8984

9085
[[nodiscard]] auto get_custom_uci_commands() const noexcept -> CommandList override
@@ -117,8 +112,6 @@ class [[nodiscard]] Engine final : public uci::EngineBase {
117112

118113
[[nodiscard]] static auto create_move_format_option() -> uci::ComboOption;
119114

120-
std::atomic_bool debugMode { false };
121-
122115
search::Thread searcher;
123116

124117
/* ----- UCI options ----- */
@@ -179,8 +172,15 @@ class [[nodiscard]] Engine final : public uci::EngineBase {
179172

180173
uci::ComboOption moveFormat { create_move_format_option() };
181174

182-
std::array<uci::Option*, 8uz> options {
183-
&ttSize, &clearTT, &ponder, &threads, &moveOverhead, &logFile, &prettyPrintMode, &moveFormat
175+
uci::BoolOption sanitizePositions {
176+
"Sanitize Positions",
177+
false,
178+
"When on, the engine checks if the position is legal before setting it.",
179+
[this](const bool sanitize) { set_sanitize_positions(sanitize); }
180+
};
181+
182+
std::array<uci::Option*, 9uz> options {
183+
&ttSize, &clearTT, &ponder, &threads, &moveOverhead, &logFile, &prettyPrintMode, &moveFormat, &sanitizePositions
184184
};
185185

186186
std::array<EngineCommand, 10uz> customCommands {

libbenbot/src/engine/Bench.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ void Engine::run_bench(const string_view arguments) const
211211

212212
do_bench(
213213
resources::get_bench_epd_text(),
214-
defaultDepth, debugMode.load());
214+
defaultDepth, is_debug_mode());
215215

216216
return;
217217
}
@@ -223,7 +223,7 @@ void Engine::run_bench(const string_view arguments) const
223223
.transform([this, defaultDepth, absPathStr = epdPath.string()](const string_view fileContent) {
224224
info_string(std::format("Running bench for {}...", absPathStr));
225225

226-
do_bench(fileContent, defaultDepth, debugMode.load());
226+
do_bench(fileContent, defaultDepth, is_debug_mode());
227227
})
228228
.transform_error(info_string);
229229
}

libbenbot/src/engine/Engine.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,7 @@ void Engine::set_pretty_printing(const bool shouldPrettyPrint)
8989
[this](const Move move) { return pretty_print_move(move); }));
9090
} else {
9191
searcher.context.set_callbacks(search::Callbacks::make_uci_printer(
92-
[this]() noexcept {
93-
return debugMode.load(memory_order_relaxed);
94-
}));
92+
[this]() noexcept { return is_debug_mode(); }));
9593
}
9694
}
9795

@@ -163,7 +161,7 @@ void Engine::write_config_file(const string_view arg) const
163161
json data;
164162

165163
data[TAG_OPTIONS] = optionsData;
166-
data[TAG_DEBUG] = debugMode.load(memory_order_relaxed);
164+
data[TAG_DEBUG] = is_debug_mode();
167165

168166
const auto filePath = absolute(path { arg });
169167

@@ -196,9 +194,8 @@ void Engine::read_config_file(const path& file)
196194
.transform([this, &filePath](const string_view fileContent) {
197195
const auto data = json::parse(fileContent);
198196

199-
debugMode.store(
200-
data.at(TAG_DEBUG).get<bool>(),
201-
memory_order_relaxed);
197+
set_debug_mode(
198+
data.at(TAG_DEBUG).get<bool>());
202199

203200
const auto& optionsData = data.at(TAG_OPTIONS);
204201

libbenbot/src/engine/Printing.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ void Engine::print_options() const
132132
print_colored_table(table);
133133

134134
println("");
135-
println("Debug mode: {}", debugMode.load());
135+
println("Debug mode: {}", is_debug_mode());
136136

137137
std::cout.flush();
138138
}

libchess/include/libchess/uci/EngineBase.hpp

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#pragma once
2121

2222
#include <array>
23+
#include <atomic>
2324
#include <functional>
2425
#include <libchess/uci/CommandParsing.hpp>
2526
#include <span>
@@ -74,6 +75,8 @@ struct EngineCommand final {
7475
}
7576
};
7677

78+
using std::memory_order_relaxed;
79+
7780
/** A base class for UCI @cite Meyer-Kahlen_2006 chess engines.
7881
7982
This class provides handling of UCI command parsing, so that
@@ -92,10 +95,10 @@ struct EngineBase {
9295

9396
virtual ~EngineBase();
9497

95-
EngineBase(const EngineBase&) = default;
96-
EngineBase(EngineBase&&) = default;
97-
EngineBase& operator=(const EngineBase&) = default;
98-
EngineBase& operator=(EngineBase&&) = default;
98+
EngineBase(const EngineBase&) = delete;
99+
EngineBase(EngineBase&&) = delete;
100+
EngineBase& operator=(const EngineBase&) = delete;
101+
EngineBase& operator=(EngineBase&&) = delete;
99102

100103
/** This must return the name of the engine.
101104
The returned string may optionally contain the engine's current version,
@@ -148,9 +151,6 @@ struct EngineBase {
148151
*/
149152
virtual void go([[maybe_unused]] const GoCommandOptions& opts) = 0;
150153

151-
/** Called when the "debug" command is received. */
152-
virtual void set_debug([[maybe_unused]] bool shouldDebug) { }
153-
154154
/** Handles a UCI command.
155155
Typically you will not call this directly, you'll just invoke ``loop()``, but this
156156
method can be used to manually invoke UCI commands if needed.
@@ -187,6 +187,30 @@ struct EngineBase {
187187
return { };
188188
}
189189

190+
/** Returns true if the engine's debugging mode is active. */
191+
[[nodiscard]] auto is_debug_mode() const noexcept -> bool
192+
{
193+
return debugMode.load(memory_order_relaxed);
194+
}
195+
196+
/** Sets the engine's debugging mode. */
197+
void set_debug_mode(const bool shouldDebug) noexcept
198+
{
199+
debugMode.store(shouldDebug, memory_order_relaxed);
200+
}
201+
202+
/** Tells the base class whether to sanitize incoming positions when processing
203+
the ``position`` command.
204+
When sanitizing is on, after evaluating each ``position`` command, the engine
205+
performs some basic checks to determine if the position is illegal, and if so,
206+
prints a diagnostic message and reverts the internal board to the previous
207+
position.
208+
*/
209+
void set_sanitize_positions(const bool shouldSanitize) noexcept
210+
{
211+
sanitizeIncomingPositions.store(shouldSanitize, memory_order_relaxed);
212+
}
213+
190214
private:
191215
void respond_to_uci();
192216
void respond_to_isready();
@@ -195,9 +219,14 @@ struct EngineBase {
195219
void handle_setpos(string_view arguments);
196220
void handle_setoption(string_view arguments);
197221

198-
bool shouldExit { false }; // used as flag for exiting the loop() function
222+
static_assert(
223+
std::atomic_bool::is_always_lock_free,
224+
"Platform doesn't support lock-free atomic operations");
199225

200-
bool initialized { false };
226+
std::atomic_bool shouldExit { false }; // used as flag for exiting the loop() function
227+
std::atomic_bool initialized { false };
228+
std::atomic_bool debugMode { false };
229+
std::atomic_bool sanitizeIncomingPositions { false };
201230

202231
Position position;
203232

@@ -249,7 +278,7 @@ struct EngineBase {
249278
.argsHelp = "[name <name>] [value <value>]" },
250279
EngineCommand {
251280
.name = "debug",
252-
.action = [this](const string_view args) { set_debug(args == "on"); },
281+
.action = [this](const string_view args) noexcept { set_debug_mode(args == "on"); },
253282
.description = "Enable/disable engine debug mode",
254283
.argsHelp = "[on|off]" },
255284
EngineCommand {

libchess/src/uci/EngineBase.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*/
1414

1515
#include <algorithm>
16+
#include <atomic>
1617
#include <expected>
1718
#include <format>
1819
#include <iostream>
@@ -31,6 +32,7 @@ namespace chess::uci {
3132

3233
using printing::info_string;
3334
using std::cout;
35+
using std::memory_order_relaxed;
3436
using std::println;
3537
using std::string_view;
3638
using util::strings::split_at_first_space;
@@ -68,7 +70,6 @@ void EngineBase::handle_command(const string_view command)
6870
}
6971

7072
info_string(std::format("Unknown UCI command: '{}'", firstWord));
71-
info_string("Type help for a list of supported commands");
7273
}
7374

7475
void EngineBase::respond_to_uci()
@@ -99,15 +100,15 @@ void EngineBase::respond_to_isready()
99100

100101
void EngineBase::respond_to_newgame()
101102
{
102-
const bool wasInitialized = std::exchange(initialized, true);
103+
const bool wasInitialized = initialized.exchange(true, memory_order_relaxed);
103104

104105
new_game(not wasInitialized);
105106
}
106107

107108
void EngineBase::handle_quit()
108109
{
109110
abort_search();
110-
shouldExit = true; // exit the event loop
111+
shouldExit.store(true, std::memory_order::release);
111112
wait();
112113
}
113114

@@ -121,13 +122,10 @@ void EngineBase::handle_setpos(const string_view arguments)
121122
// we print an error message via `info string` and keep the old position.
122123
// See this Stockfish PR discussion: https://github.com/official-stockfish/Stockfish/pull/4563
123124

124-
// NB. enabling this check seems to cost about 8 ELO
125-
static constexpr bool SanitizeIncomingPositions = false;
126-
127125
[[maybe_unused]] const auto obj
128126
= parse_position_options(arguments)
129127
.and_then([this](const Position& pos) -> std::expected<void, std::string> {
130-
if constexpr (SanitizeIncomingPositions) {
128+
if (sanitizeIncomingPositions.load(memory_order_relaxed)) {
131129
if (const auto errorStr = pos.is_illegal()) {
132130
[[unlikely]];
133131
return std::unexpected {
@@ -200,7 +198,7 @@ void EngineBase::loop()
200198
std::getline(std::cin, inputBuf);
201199

202200
handle_command(inputBuf);
203-
} while (not shouldExit);
201+
} while (not shouldExit.load(std::memory_order::acquire));
204202
}
205203

206204
} // namespace chess::uci

0 commit comments

Comments
 (0)