Skip to content

Commit b10efc5

Browse files
committed
refactor: standard UCI options into engine base class
1 parent 7a3e63a commit b10efc5

File tree

4 files changed

+114
-59
lines changed

4 files changed

+114
-59
lines changed

libbenbot/include/libbenbot/engine/Engine.hpp

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

2222
#include <array>
23-
#include <atomic>
2423
#include <filesystem>
2524
#include <functional>
2625
#include <libbenbot/search/Thread.hpp>
@@ -80,7 +79,10 @@ class [[nodiscard]] Engine final : public uci::EngineBase {
8079

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

83-
[[nodiscard]] auto get_options() -> std::span<uci::Option*> override { return options; }
82+
[[nodiscard]] auto get_custom_uci_options() noexcept -> OptionList override
83+
{
84+
return options;
85+
}
8486

8587
[[nodiscard]] auto get_custom_uci_commands() const noexcept -> CommandList override
8688
{
@@ -112,41 +114,29 @@ class [[nodiscard]] Engine final : public uci::EngineBase {
112114

113115
[[nodiscard]] static auto create_move_format_option() -> uci::ComboOption;
114116

117+
void resize_transposition_table(const size_t sizeMB) override
118+
{
119+
searcher.context.resize_transposition_table(sizeMB);
120+
}
121+
122+
void set_ponder(const bool shouldPonder) override
123+
{
124+
// the ponder flag is only ever turned on via the go options,
125+
// but it can be turned off by disabling this UCI option
126+
if (not shouldPonder)
127+
searcher.context.set_pondering(false);
128+
}
129+
115130
search::Thread searcher;
116131

117132
/* ----- UCI options ----- */
118133

119-
uci::IntOption ttSize {
120-
"Hash",
121-
1, 2048, 16,
122-
"Sets the maximum transposition table size (in MB).",
123-
[this](const int sizeMB) {
124-
searcher.context.resize_transposition_table(static_cast<size_t>(sizeMB));
125-
}
126-
};
127-
128134
uci::Action clearTT {
129135
"Clear Hash",
130136
[this] { searcher.context.clear_transposition_table(); },
131137
"Press to clear the transposition table."
132138
};
133139

134-
// The engine doesn't start pondering on its own without explicitly being told to
135-
// via another go command; this option is needed to inform the GUI that the engine
136-
// supports pondering, and also gives the engine the opportunity to adjust its time
137-
// management algorithm when pondering is enabled.
138-
uci::BoolOption ponder {
139-
"Ponder",
140-
false,
141-
"Controls whether pondering is allowed.",
142-
[this](const bool shouldPonder) {
143-
// the ponder flag is only ever turned on via the go options,
144-
// but it can be turned off by disabling this UCI option
145-
if (not shouldPonder)
146-
searcher.context.set_pondering(false);
147-
}
148-
};
149-
150140
uci::IntOption threads {
151141
"Threads", 1, 1, 1,
152142
"Number of searcher threads (currently a dummy)."
@@ -179,8 +169,8 @@ class [[nodiscard]] Engine final : public uci::EngineBase {
179169
[this](const bool sanitize) { set_sanitize_positions(sanitize); }
180170
};
181171

182-
std::array<uci::Option*, 9uz> options {
183-
&ttSize, &clearTT, &ponder, &threads, &moveOverhead, &logFile, &prettyPrintMode, &moveFormat, &sanitizePositions
172+
std::array<uci::Option*, 7uz> options {
173+
&clearTT, &threads, &moveOverhead, &logFile, &prettyPrintMode, &moveFormat, &sanitizePositions
184174
};
185175

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

libbenbot/src/engine/Engine.cpp

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

1515
#include <algorithm>
16-
#include <atomic>
1716
#include <chrono>
1817
#include <cstddef> // IWYU pragma: keep - for size_t
1918
#include <filesystem>
@@ -39,7 +38,6 @@
3938
namespace ben_bot {
4039

4140
using std::filesystem::path;
42-
using std::memory_order_relaxed;
4341
using std::size_t;
4442
using uci::printing::info_string;
4543

@@ -103,7 +101,7 @@ void Engine::go(const uci::GoCommandOptions& opts)
103101
searcher.context.set_options(newOpts);
104102

105103
searcher.context.set_pondering(
106-
opts.ponderMode and ponder.get_value());
104+
opts.ponderMode and opt_Ponder.get_value());
107105

108106
searcher.start();
109107
}

libchess/include/libchess/uci/EngineBase.hpp

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121

2222
#include <array>
2323
#include <atomic>
24+
#include <cassert>
2425
#include <functional>
2526
#include <libchess/uci/CommandParsing.hpp>
27+
#include <libchess/uci/Options.hpp>
2628
#include <span>
2729
#include <string>
2830
#include <string_view>
@@ -37,8 +39,6 @@ namespace chess::uci {
3739
using game::Position;
3840
using std::string_view;
3941

40-
struct Option;
41-
4242
/** A UCI command that the engine can respond to.
4343
@ingroup uci
4444
*/
@@ -75,8 +75,6 @@ struct EngineCommand final {
7575
}
7676
};
7777

78-
using std::memory_order_relaxed;
79-
8078
/** A base class for UCI @cite Meyer-Kahlen_2006 chess engines.
8179
8280
This class provides handling of UCI command parsing, so that
@@ -109,9 +107,6 @@ struct EngineBase {
109107
/** This must return the name of the engine's author. */
110108
[[nodiscard]] virtual auto get_author() const -> string_view = 0;
111109

112-
/** This must return the list of all options the engine supports. */
113-
[[nodiscard]] virtual auto get_options() -> std::span<Option*> { return { }; }
114-
115110
/** This function will be called when the "isready" command is received,
116111
and may block while waiting for background tasks to complete. This
117112
function should be thread-safe.
@@ -187,16 +182,31 @@ struct EngineBase {
187182
return { };
188183
}
189184

185+
/** Typedef for a view of a list of engine options. */
186+
using OptionList = std::span<Option*>;
187+
188+
/** Subclasses should overload this to return their custom UCI options. */
189+
[[nodiscard]] virtual auto get_custom_uci_options() noexcept -> OptionList
190+
{
191+
return { };
192+
}
193+
194+
/** Returns the engine's list of supported standard UCI commands. */
195+
[[nodiscard]] auto get_standard_uci_options() noexcept -> OptionList
196+
{
197+
return standardUCIOptions;
198+
}
199+
190200
/** Returns true if the engine's debugging mode is active. */
191201
[[nodiscard]] auto is_debug_mode() const noexcept -> bool
192202
{
193-
return debugMode.load(memory_order_relaxed);
203+
return debugMode.load(std::memory_order_relaxed);
194204
}
195205

196206
/** Sets the engine's debugging mode. */
197207
void set_debug_mode(const bool shouldDebug) noexcept
198208
{
199-
debugMode.store(shouldDebug, memory_order_relaxed);
209+
debugMode.store(shouldDebug, std::memory_order_relaxed);
200210
}
201211

202212
/** Tells the base class whether to sanitize incoming positions when processing
@@ -208,9 +218,44 @@ struct EngineBase {
208218
*/
209219
void set_sanitize_positions(const bool shouldSanitize) noexcept
210220
{
211-
sanitizeIncomingPositions.store(shouldSanitize, memory_order_relaxed);
221+
sanitizeIncomingPositions.store(shouldSanitize, std::memory_order_release);
212222
}
213223

224+
/** Subclasses should implement this to resize their transposition table. */
225+
virtual void resize_transposition_table([[maybe_unused]] const size_t sizeMB) { }
226+
227+
/** Subclasses can implement this to be informed when the ponder parameter changes. */
228+
virtual void set_ponder([[maybe_unused]] const bool shouldPonder) { }
229+
230+
//// @name Standard UCI options
231+
/// @{
232+
233+
/** Standard UCI option for hash size. */
234+
IntOption opt_Hash {
235+
"Hash",
236+
1, 2048, 16,
237+
"Sets the transposition table size (in MB)",
238+
[this](const int sizeMB) {
239+
assert(sizeMB >= 0);
240+
resize_transposition_table(static_cast<size_t>(sizeMB));
241+
}
242+
};
243+
244+
/** Standard UCI option for pondering.
245+
The engine doesn't start pondering on its own without explicitly being told to
246+
via another ``go`` command; this option is needed to inform the GUI that the engine
247+
supports pondering, and also gives the engine the opportunity to adjust its time
248+
management algorithm when pondering is enabled.
249+
*/
250+
BoolOption opt_Ponder {
251+
"Ponder",
252+
false,
253+
"Controls whether pondering is allowed.",
254+
[this](const bool shouldPonder) { set_ponder(shouldPonder); }
255+
};
256+
257+
/// @}
258+
214259
private:
215260
void respond_to_uci();
216261
void respond_to_isready();
@@ -289,6 +334,10 @@ struct EngineBase {
289334
.description = "Handle license registration",
290335
.argsHelp = { } }
291336
};
337+
338+
std::array<Option*, 2uz> standardUCIOptions {
339+
&opt_Hash, &opt_Ponder
340+
};
292341
};
293342

294343
} // namespace chess::uci

libchess/src/uci/EngineBase.cpp

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include <algorithm>
1616
#include <atomic>
17+
#include <cassert>
1718
#include <expected>
1819
#include <format>
1920
#include <iostream>
@@ -32,7 +33,9 @@ namespace chess::uci {
3233

3334
using printing::info_string;
3435
using std::cout;
36+
using std::memory_order_acquire;
3537
using std::memory_order_relaxed;
38+
using std::memory_order_release;
3639
using std::println;
3740
using std::string_view;
3841
using util::strings::split_at_first_space;
@@ -79,7 +82,7 @@ void EngineBase::respond_to_uci()
7982
println(cout, "id name {}", get_name());
8083
println(cout, "id author {}", get_author());
8184

82-
for (const auto* option : get_options())
85+
for (const auto* option : get_custom_uci_options())
8386
println(cout, "{}", option->get_declaration_string());
8487

8588
println(cout, "uciok");
@@ -95,6 +98,7 @@ void EngineBase::respond_to_isready()
9598
wait();
9699

97100
println(cout, "readyok");
101+
98102
cout.flush();
99103
}
100104

@@ -108,7 +112,9 @@ void EngineBase::respond_to_newgame()
108112
void EngineBase::handle_quit()
109113
{
110114
abort_search();
111-
shouldExit.store(true, std::memory_order::release);
115+
116+
shouldExit.store(true, memory_order_release);
117+
112118
wait();
113119
}
114120

@@ -125,7 +131,7 @@ void EngineBase::handle_setpos(const string_view arguments)
125131
[[maybe_unused]] const auto obj
126132
= parse_position_options(arguments)
127133
.and_then([this](const Position& pos) -> std::expected<void, std::string> {
128-
if (sanitizeIncomingPositions.load(memory_order_relaxed)) {
134+
if (sanitizeIncomingPositions.load(memory_order_acquire)) {
129135
if (const auto errorStr = pos.is_illegal()) {
130136
[[unlikely]];
131137
return std::unexpected {
@@ -173,21 +179,33 @@ void EngineBase::handle_setoption(const string_view arguments)
173179

174180
wait();
175181

176-
const auto options = get_options();
182+
auto update_option = [name, isNPos, rest, valueTokenIdx](const OptionList options) {
183+
if (const auto it = std::ranges::find_if(
184+
options,
185+
[name](const Option* opt) { return opt->get_name() == name; });
186+
it != options.end()) {
187+
auto* option = *it;
177188

178-
if (const auto it = std::ranges::find_if(
179-
options,
180-
[name](const Option* opt) { return opt->get_name() == name; });
181-
it != options.end()) {
182-
auto* option = *it;
189+
assert(option != nullptr);
183190

184-
if (isNPos)
185-
option->handle_setvalue({ });
186-
else
187-
option->handle_setvalue(trim(rest.substr(valueTokenIdx)));
188-
} else {
189-
info_string(std::format("Attempted to set unknown option '{}'", name));
190-
}
191+
if (isNPos)
192+
option->handle_setvalue({ });
193+
else
194+
option->handle_setvalue(trim(rest.substr(valueTokenIdx)));
195+
196+
return true;
197+
}
198+
199+
return false;
200+
};
201+
202+
if (update_option(standardUCIOptions))
203+
return;
204+
205+
if (update_option(get_custom_uci_options()))
206+
return;
207+
208+
info_string(std::format("Attempted to set unknown option '{}'", name));
191209
}
192210

193211
void EngineBase::loop()
@@ -198,7 +216,7 @@ void EngineBase::loop()
198216
std::getline(std::cin, inputBuf);
199217

200218
handle_command(inputBuf);
201-
} while (not shouldExit.load(std::memory_order::acquire));
219+
} while (not shouldExit.load(memory_order_acquire));
202220
}
203221

204222
} // namespace chess::uci

0 commit comments

Comments
 (0)