Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
official-stockfish
GitHub Repository: official-stockfish/Stockfish
Path: blob/master/src/tune.h
376 views
1
/*
2
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
4
5
Stockfish is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9
10
Stockfish is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
14
15
You should have received a copy of the GNU General Public License
16
along with this program. If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#ifndef TUNE_H_INCLUDED
20
#define TUNE_H_INCLUDED
21
22
#include <cstddef>
23
#include <memory>
24
#include <string>
25
#include <type_traits> // IWYU pragma: keep
26
#include <utility>
27
#include <vector>
28
29
namespace Stockfish {
30
31
class OptionsMap;
32
33
using Range = std::pair<int, int>; // Option's min-max values
34
using RangeFun = Range(int);
35
36
// Default Range function, to calculate Option's min-max values
37
inline Range default_range(int v) { return v > 0 ? Range(0, 2 * v) : Range(2 * v, 0); }
38
39
struct SetRange {
40
explicit SetRange(RangeFun f) :
41
fun(f) {}
42
SetRange(int min, int max) :
43
fun(nullptr),
44
range(min, max) {}
45
Range operator()(int v) const { return fun ? fun(v) : range; }
46
47
RangeFun* fun;
48
Range range;
49
};
50
51
#define SetDefaultRange SetRange(default_range)
52
53
54
// Tune class implements the 'magic' code that makes the setup of a fishtest tuning
55
// session as easy as it can be. Mainly you have just to remove const qualifiers
56
// from the variables you want to tune and flag them for tuning, so if you have:
57
//
58
// const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } };
59
//
60
// If you have a my_post_update() function to run after values have been updated,
61
// and a my_range() function to set custom Option's min-max values, then you just
62
// remove the 'const' qualifiers and write somewhere below in the file:
63
//
64
// TUNE(SetRange(my_range), myValue, my_post_update);
65
//
66
// You can also set the range directly, and restore the default at the end
67
//
68
// TUNE(SetRange(-100, 100), myValue, SetDefaultRange);
69
//
70
// In case update function is slow and you have many parameters, you can add:
71
//
72
// UPDATE_ON_LAST();
73
//
74
// And the values update, including post update function call, will be done only
75
// once, after the engine receives the last UCI option, that is the one defined
76
// and created as the last one, so the GUI should send the options in the same
77
// order in which have been defined.
78
79
class Tune {
80
81
using PostUpdate = void(); // Post-update function
82
83
Tune() { read_results(); }
84
Tune(const Tune&) = delete;
85
void operator=(const Tune&) = delete;
86
void read_results();
87
88
static Tune& instance() {
89
static Tune t;
90
return t;
91
} // Singleton
92
93
// Use polymorphism to accommodate Entry of different types in the same vector
94
struct EntryBase {
95
virtual ~EntryBase() = default;
96
virtual void init_option() = 0;
97
virtual void read_option() = 0;
98
};
99
100
template<typename T>
101
struct Entry: public EntryBase {
102
103
static_assert(!std::is_const_v<T>, "Parameter cannot be const!");
104
105
static_assert(std::is_same_v<T, int> || std::is_same_v<T, PostUpdate>,
106
"Parameter type not supported!");
107
108
Entry(const std::string& n, T& v, const SetRange& r) :
109
name(n),
110
value(v),
111
range(r) {}
112
void operator=(const Entry&) = delete; // Because 'value' is a reference
113
void init_option() override;
114
void read_option() override;
115
116
std::string name;
117
T& value;
118
SetRange range;
119
};
120
121
// Our facility to fill the container, each Entry corresponds to a parameter
122
// to tune. We use variadic templates to deal with an unspecified number of
123
// entries, each one of a possible different type.
124
static std::string next(std::string& names, bool pop = true);
125
126
int add(const SetRange&, std::string&&) { return 0; }
127
128
template<typename T, typename... Args>
129
int add(const SetRange& range, std::string&& names, T& value, Args&&... args) {
130
list.push_back(std::unique_ptr<EntryBase>(new Entry<T>(next(names), value, range)));
131
return add(range, std::move(names), args...);
132
}
133
134
// Template specialization for arrays: recursively handle multi-dimensional arrays
135
template<typename T, size_t N, typename... Args>
136
int add(const SetRange& range, std::string&& names, T (&value)[N], Args&&... args) {
137
for (size_t i = 0; i < N; i++)
138
add(range, next(names, i == N - 1) + "[" + std::to_string(i) + "]", value[i]);
139
return add(range, std::move(names), args...);
140
}
141
142
// Template specialization for SetRange
143
template<typename... Args>
144
int add(const SetRange&, std::string&& names, SetRange& value, Args&&... args) {
145
return add(value, (next(names), std::move(names)), args...);
146
}
147
148
static void make_option(OptionsMap* options, const std::string& n, int v, const SetRange& r);
149
150
std::vector<std::unique_ptr<EntryBase>> list;
151
152
public:
153
template<typename... Args>
154
static int add(const std::string& names, Args&&... args) {
155
return instance().add(SetDefaultRange, names.substr(1, names.size() - 2),
156
args...); // Remove trailing parenthesis
157
}
158
static void init(OptionsMap& o) {
159
options = &o;
160
for (auto& e : instance().list)
161
e->init_option();
162
read_options();
163
} // Deferred, due to UCIEngine::Options access
164
static void read_options() {
165
for (auto& e : instance().list)
166
e->read_option();
167
}
168
169
static bool update_on_last;
170
static OptionsMap* options;
171
};
172
173
template<typename... Args>
174
constexpr void tune_check_args(Args&&...) {
175
static_assert((!std::is_fundamental_v<Args> && ...), "TUNE macro arguments wrong");
176
}
177
178
// Some macro magic :-) we define a dummy int variable that the compiler initializes calling Tune::add()
179
#define STRINGIFY(x) #x
180
#define UNIQUE2(x, y) x##y
181
#define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__
182
#define TUNE(...) \
183
int UNIQUE(p, __LINE__) = []() -> int { \
184
tune_check_args(__VA_ARGS__); \
185
return Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__); \
186
}();
187
188
#define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true
189
190
} // namespace Stockfish
191
192
#endif // #ifndef TUNE_H_INCLUDED
193
194