Skip to content

Commit 8c6fec3

Browse files
authored
refactor: search context encapsulation (#481)
2 parents 2ede8b5 + 5fec5cb commit 8c6fec3

File tree

14 files changed

+187
-152
lines changed

14 files changed

+187
-152
lines changed

.github/workflows/sprt_internal.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ jobs:
4343
env:
4444
CMAKE_PRESET: gcc
4545
CMAKE_BUILD_PARALLEL_LEVEL: 8
46-
BUILD_DIR: ${{ github.workspace }}/Builds/clang
46+
BUILD_DIR: ${{ github.workspace }}/Builds/gcc
4747
SPRT_OUTPUT_LOG: ${{ github.workspace }}/logs/sprt/output.log
4848
# these directories are used for building & installing the latest
4949
# main, in the case that we're using main as the SPRT baseline
5050
TMP_ROOT: ${{ github.workspace }}/tmp-main
51-
TMP_BUILD_DIR: ${{ github.workspace }}/tmp-main/Builds/clang
51+
TMP_BUILD_DIR: ${{ github.workspace }}/tmp-main/Builds/gcc
5252
TMP_DEPLOY_DIR: ${{ github.workspace }}/tmp-main/deploy
5353

5454
steps:

libbenbot/include/libbenbot/data-structures/TranspositionTable.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class TranspositionTable final {
104104
TranspositionTable& operator=(TranspositionTable&& other) noexcept;
105105

106106
/** Retrieves the stored record for the given position,
107-
or nullptr if the given position isn't in the table.
107+
or ``nullopt`` if the given position isn't in the table.
108108
*/
109109
[[nodiscard]] auto find(const Position& pos) const -> std::optional<TTData>;
110110

libbenbot/include/libbenbot/engine/Engine.hpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@
2626
#include <libbenbot/engine/CustomCommand.hpp>
2727
#include <libbenbot/search/Thread.hpp>
2828
#include <libchess/game/Position.hpp>
29-
#include <libchess/uci/CommandParsing.hpp>
3029
#include <libchess/uci/EngineBase.hpp>
3130
#include <libchess/uci/Options.hpp>
3231
#include <span>
3332
#include <string>
3433
#include <string_view>
3534

35+
namespace chess::uci {
36+
struct GoCommandOptions;
37+
} // namespace chess::uci
38+
3639
namespace ben_bot {
3740

3841
using chess::game::Position;
@@ -65,7 +68,7 @@ class [[nodiscard]] Engine final : public uci::EngineBase {
6568

6669
void new_game(bool firstCall) override;
6770

68-
void set_position(const Position& pos) override { searcher.set_position(pos); }
71+
void set_position(const Position& pos) override { searcher.context.set_position(pos); }
6972

7073
void go(const uci::GoCommandOptions& opts) override;
7174

@@ -122,9 +125,7 @@ class [[nodiscard]] Engine final : public uci::EngineBase {
122125
1, 2048, 16,
123126
"Sets the maximum transposition table size (in MB).",
124127
[this](const int sizeMB) {
125-
wait();
126-
searcher.context.transTable.resize(
127-
static_cast<size_t>(sizeMB));
128+
searcher.context.resize_transposition_table(static_cast<size_t>(sizeMB));
128129
}
129130
};
130131

@@ -141,7 +142,13 @@ class [[nodiscard]] Engine final : public uci::EngineBase {
141142
uci::BoolOption ponder {
142143
"Ponder",
143144
false,
144-
"Controls whether pondering is allowed."
145+
"Controls whether pondering is allowed.",
146+
[this](const bool shouldPonder) {
147+
// the ponder flag is only ever turned on via the go options,
148+
// but it can be turned off by disabling this UCI option
149+
if (not shouldPonder)
150+
searcher.context.set_pondering(false);
151+
}
145152
};
146153

147154
uci::IntOption threads {

libbenbot/include/libbenbot/search/Context.hpp

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,14 @@
2424
#include <libbenbot/data-structures/TranspositionTable.hpp>
2525
#include <libbenbot/search/Callbacks.hpp>
2626
#include <libbenbot/search/Options.hpp>
27+
#include <libchess/game/Position.hpp>
28+
#include <optional>
2729
#include <utility>
2830

2931
namespace ben_bot::search {
3032

33+
using chess::game::Position;
34+
3135
/** This struct encapsulates everything needed to perform a search.
3236
You can keep one of these alive between searches by simply updating
3337
the options and then calling ``search()`` again.
@@ -50,31 +54,13 @@ struct Context final {
5054
Context(Context&&) = delete;
5155
Context& operator=(Context&&) = delete;
5256

53-
/** The options to use for the search.
54-
This object can only be safely mutated when no search is executing.
55-
*/
56-
Options options;
57-
58-
/** The transposition table used for this search.
59-
This object's methods can only be safely called when no search is executing.
60-
*/
61-
TranspositionTable transTable;
62-
63-
/** The callbacks used to provide results about the search. */
64-
Callbacks callbacks;
65-
6657
/** Performs a search.
6758
Results will be propagated via the ``callbacks`` that have been
6859
assigned.
6960
7061
The search may execute for a potentially unbounded amount of time.
7162
The search can be interrupted by calling the ``abort()`` method while
7263
``search()`` is executing.
73-
74-
This function accesses ``options`` and ``transTable``; these objects
75-
must not be mutated while ``search()`` is executing. ``abort()``,
76-
``wait()``, ``in_progress()``, and ``reset()`` may be called while
77-
``search()`` is executing without introducing data races.
7864
*/
7965
void search();
8066

@@ -88,14 +74,29 @@ struct Context final {
8874

8975
/** Clears the transposition table.
9076
If a search is in progress, this method blocks until it returns.
91-
Invoking this method is thread-safe, even if a search was in progress.
9277
*/
9378
void clear_transposition_table()
9479
{
9580
wait();
9681
transTable.clear();
9782
}
9883

84+
/** Resizes the transposition table.
85+
If a search is in progress, this method blocks until it returns.
86+
*/
87+
void resize_transposition_table(size_t sizeMB)
88+
{
89+
wait();
90+
transTable.resize(sizeMB);
91+
}
92+
93+
/** Probes the transposition table for the given position. */
94+
[[nodiscard]] auto probe_transposition_table(const Position& pos) const
95+
-> std::optional<TTData>
96+
{
97+
return transTable.find(pos);
98+
}
99+
99100
/** Returns true if a search is currently in progress. */
100101
[[nodiscard]] auto in_progress() const noexcept -> bool { return activeFlag.load(std::memory_order::acquire); }
101102

@@ -115,13 +116,63 @@ struct Context final {
115116
*/
116117
void ponder_hit() noexcept { pondering.store(false, std::memory_order::release); }
117118

119+
/** Sets the position to be searched by the next search.
120+
If a search is in progress, this function blocks until it completes.
121+
*/
122+
void set_position(const Position& pos)
123+
{
124+
wait();
125+
position = pos;
126+
127+
// clear this so that all legal moves will be searched by default
128+
options.movesToSearch.clear();
129+
}
130+
131+
/** Sets the options to be used by the next search.
132+
If a search is in progress, this function blocks until it completes.
133+
*/
134+
void set_options(const Options& opts)
135+
{
136+
wait();
137+
options = opts;
138+
}
139+
140+
/** Sets the options to be used by the next search.
141+
If a search is in progress, this function blocks until it completes.
142+
*/
143+
void set_options(const chess::uci::GoCommandOptions& opts)
144+
{
145+
wait();
146+
options = Options::from_libchess(opts, position.is_white_to_move());
147+
}
148+
149+
/** Sets the result callbacks that will be used for the next search.
150+
If a search is in progress, this function blocks until it completes.
151+
*/
152+
void set_callbacks(Callbacks&& callbacksToUse)
153+
{
154+
wait();
155+
callbacks = std::move(callbacksToUse);
156+
}
157+
158+
/** Returns the current position. */
159+
[[nodiscard]] auto get_position() const noexcept -> const Position& { return position; }
160+
118161
private:
119162
std::atomic_bool exitFlag { false };
120163

121164
std::atomic_bool activeFlag { false };
122165

123166
std::atomic_bool pondering { false };
124167

168+
Position position;
169+
170+
Options options;
171+
172+
Callbacks callbacks;
173+
174+
TranspositionTable transTable;
175+
125176
KillerMoves killerMoves;
126177
};
127178

libbenbot/include/libbenbot/search/Options.hpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727

2828
#include <chrono>
2929
#include <cstddef> // IWYU pragma: keep - for size_t
30-
#include <libchess/game/Position.hpp>
3130
#include <libchess/moves/MoveGen.hpp>
3231
#include <limits>
3332
#include <optional>
@@ -51,9 +50,6 @@ using std::size_t;
5150
@see Context
5251
*/
5352
struct Options final {
54-
/** The root position to be searched. */
55-
chess::game::Position position;
56-
5753
/** The maximum search depth (in plies). */
5854
size_t depth { std::numeric_limits<size_t>::max() };
5955

@@ -81,8 +77,10 @@ struct Options final {
8177
/** Search for mate in this many moves. */
8278
std::optional<size_t> mateIn;
8379

84-
/** Updates the values in this options struct with the UCI "go" command options. */
85-
void update_from(const chess::uci::GoCommandOptions& goOptions);
80+
/** Translates the raw UCI "go" command options into this struct's fields. */
81+
[[nodiscard]] static auto from_libchess(
82+
const chess::uci::GoCommandOptions& goOptions,
83+
bool isWhiteToMove) -> Options;
8684
};
8785

8886
} // namespace ben_bot::search

libbenbot/include/libbenbot/search/Thread.hpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,16 @@ struct Thread final {
6060
*/
6161
Context context;
6262

63-
/** Sets the position to be searched by the next search invocation.
64-
This method blocks waiting for any previously executing search to complete.
65-
*/
66-
void set_position(const Position& pos);
67-
6863
/** Begins searching asynchronously.
6964
This method returns immediately, and the actual search will be performed by
7065
a background thread.
7166
*/
72-
/// @{
73-
void start(
74-
const chess::uci::GoCommandOptions& options,
75-
milliseconds moveOverheadTime = milliseconds { 0uz });
67+
void start()
68+
{
69+
context.wait(); // shouldn't have been searching, but better safe than sorry
7670

77-
void start();
78-
/// @}
71+
startSearch.store(true, std::memory_order::release);
72+
}
7973

8074
private:
8175
void thread_func();

libbenbot/src/engine/Bench.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#include <algorithm>
1616
#include <cassert>
1717
#include <chrono>
18-
#include <cmath>
18+
#include <cmath> // IWYU pragma: keep - for std::round()
1919
#include <cstddef> // IWYU pragma: keep - for size_t
2020
#include <expected>
2121
#include <filesystem>
@@ -24,6 +24,7 @@
2424
#include <libbenbot/Resources.hpp>
2525
#include <libbenbot/engine/Engine.hpp>
2626
#include <libbenbot/search/Callbacks.hpp>
27+
#include <libbenbot/search/Options.hpp>
2728
#include <libbenbot/search/Result.hpp>
2829
#include <libbenbot/search/Thread.hpp>
2930
#include <libchess/notation/EPD.hpp>
@@ -66,15 +67,19 @@ namespace {
6667
: threadNumber { threadNum }
6768
, outputProgress { printProgressOutput }
6869
{
69-
thread.set_position(position.position);
70+
thread.context.set_position(position.position);
71+
72+
search::Options options;
7073

7174
if (const auto it = position.operations.find("depth");
7275
it != position.operations.end()) {
73-
thread.context.options.depth = util::strings::int_from_string(it->second, defaultDepth);
76+
options.depth = util::strings::int_from_string(it->second, defaultDepth);
7477
} else {
75-
thread.context.options.depth = defaultDepth;
78+
options.depth = defaultDepth;
7679
}
7780

81+
thread.context.set_options(options);
82+
7883
thread.start();
7984
}
8085

0 commit comments

Comments
 (0)