]> git.sesse.net Git - stockfish/blob - src/tune.h
Fix compilation after recent merge.
[stockfish] / src / tune.h
1 /*
2   Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3   Copyright (C) 2004-2023 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 enum Value : int;
31
32 using Range    = std::pair<int, int>;  // Option's min-max values
33 using RangeFun = Range(int);
34
35 // Default Range function, to calculate Option's min-max values
36 inline Range default_range(int v) { return v > 0 ? Range(0, 2 * v) : Range(2 * v, 0); }
37
38 struct SetRange {
39     explicit SetRange(RangeFun f) :
40         fun(f) {}
41     SetRange(int min, int max) :
42         fun(nullptr),
43         range(min, max) {}
44     Range operator()(int v) const { return fun ? fun(v) : range; }
45
46     RangeFun* fun;
47     Range     range;
48 };
49
50 #define SetDefaultRange SetRange(default_range)
51
52
53 // Tune class implements the 'magic' code that makes the setup of a fishtest tuning
54 // session as easy as it can be. Mainly you have just to remove const qualifiers
55 // from the variables you want to tune and flag them for tuning, so if you have:
56 //
57 //   const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } };
58 //
59 // If you have a my_post_update() function to run after values have been updated,
60 // and a my_range() function to set custom Option's min-max values, then you just
61 // remove the 'const' qualifiers and write somewhere below in the file:
62 //
63 //   TUNE(SetRange(my_range), myValue, my_post_update);
64 //
65 // You can also set the range directly, and restore the default at the end
66 //
67 //   TUNE(SetRange(-100, 100), myValue, SetDefaultRange);
68 //
69 // In case update function is slow and you have many parameters, you can add:
70 //
71 //   UPDATE_ON_LAST();
72 //
73 // And the values update, including post update function call, will be done only
74 // once, after the engine receives the last UCI option, that is the one defined
75 // and created as the last one, so the GUI should send the options in the same
76 // order in which have been defined.
77
78 class Tune {
79
80     using PostUpdate = void();  // Post-update function
81
82     Tune() { read_results(); }
83     Tune(const Tune&)           = delete;
84     void operator=(const Tune&) = delete;
85     void read_results();
86
87     static Tune& instance() {
88         static Tune t;
89         return t;
90     }  // Singleton
91
92     // Use polymorphism to accommodate Entry of different types in the same vector
93     struct EntryBase {
94         virtual ~EntryBase()       = default;
95         virtual void init_option() = 0;
96         virtual void read_option() = 0;
97     };
98
99     template<typename T>
100     struct Entry: public EntryBase {
101
102         static_assert(!std::is_const_v<T>, "Parameter cannot be const!");
103
104         static_assert(std::is_same_v<T, int> || std::is_same_v<T, Value>
105                         || 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     std::vector<std::unique_ptr<EntryBase>> list;
149
150    public:
151     template<typename... Args>
152     static int add(const std::string& names, Args&&... args) {
153         return instance().add(SetDefaultRange, names.substr(1, names.size() - 2),
154                               args...);  // Remove trailing parenthesis
155     }
156     static void init() {
157         for (auto& e : instance().list)
158             e->init_option();
159         read_options();
160     }  // Deferred, due to UCI::Options access
161     static void read_options() {
162         for (auto& e : instance().list)
163             e->read_option();
164     }
165     static bool update_on_last;
166 };
167
168 // Some macro magic :-) we define a dummy int variable that the compiler initializes calling Tune::add()
169 #define STRINGIFY(x) #x
170 #define UNIQUE2(x, y) x##y
171 #define UNIQUE(x, y) UNIQUE2(x, y)  // Two indirection levels to expand __LINE__
172 #define TUNE(...) int UNIQUE(p, __LINE__) = Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__)
173
174 #define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true
175
176 }  // namespace Stockfish
177
178 #endif  // #ifndef TUNE_H_INCLUDED