/*1Stockfish, a UCI chess playing engine derived from Glaurung 2.12Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)34Stockfish is free software: you can redistribute it and/or modify5it under the terms of the GNU General Public License as published by6the Free Software Foundation, either version 3 of the License, or7(at your option) any later version.89Stockfish is distributed in the hope that it will be useful,10but WITHOUT ANY WARRANTY; without even the implied warranty of11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12GNU General Public License for more details.1314You should have received a copy of the GNU General Public License15along with this program. If not, see <http://www.gnu.org/licenses/>.16*/1718#ifndef TUNE_H_INCLUDED19#define TUNE_H_INCLUDED2021#include <cstddef>22#include <memory>23#include <string>24#include <type_traits> // IWYU pragma: keep25#include <utility>26#include <vector>2728namespace Stockfish {2930class OptionsMap;3132using Range = std::pair<int, int>; // Option's min-max values33using RangeFun = Range(int);3435// Default Range function, to calculate Option's min-max values36inline Range default_range(int v) { return v > 0 ? Range(0, 2 * v) : Range(2 * v, 0); }3738struct SetRange {39explicit SetRange(RangeFun f) :40fun(f) {}41SetRange(int min, int max) :42fun(nullptr),43range(min, max) {}44Range operator()(int v) const { return fun ? fun(v) : range; }4546RangeFun* fun;47Range range;48};4950#define SetDefaultRange SetRange(default_range)515253// Tune class implements the 'magic' code that makes the setup of a fishtest tuning54// session as easy as it can be. Mainly you have just to remove const qualifiers55// 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 just61// 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 end66//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 only74// once, after the engine receives the last UCI option, that is the one defined75// and created as the last one, so the GUI should send the options in the same76// order in which have been defined.7778class Tune {7980using PostUpdate = void(); // Post-update function8182Tune() { read_results(); }83Tune(const Tune&) = delete;84void operator=(const Tune&) = delete;85void read_results();8687static Tune& instance() {88static Tune t;89return t;90} // Singleton9192// Use polymorphism to accommodate Entry of different types in the same vector93struct EntryBase {94virtual ~EntryBase() = default;95virtual void init_option() = 0;96virtual void read_option() = 0;97};9899template<typename T>100struct Entry: public EntryBase {101102static_assert(!std::is_const_v<T>, "Parameter cannot be const!");103104static_assert(std::is_same_v<T, int> || std::is_same_v<T, PostUpdate>,105"Parameter type not supported!");106107Entry(const std::string& n, T& v, const SetRange& r) :108name(n),109value(v),110range(r) {}111void operator=(const Entry&) = delete; // Because 'value' is a reference112void init_option() override;113void read_option() override;114115std::string name;116T& value;117SetRange range;118};119120// Our facility to fill the container, each Entry corresponds to a parameter121// to tune. We use variadic templates to deal with an unspecified number of122// entries, each one of a possible different type.123static std::string next(std::string& names, bool pop = true);124125int add(const SetRange&, std::string&&) { return 0; }126127template<typename T, typename... Args>128int add(const SetRange& range, std::string&& names, T& value, Args&&... args) {129list.push_back(std::unique_ptr<EntryBase>(new Entry<T>(next(names), value, range)));130return add(range, std::move(names), args...);131}132133// Template specialization for arrays: recursively handle multi-dimensional arrays134template<typename T, size_t N, typename... Args>135int add(const SetRange& range, std::string&& names, T (&value)[N], Args&&... args) {136for (size_t i = 0; i < N; i++)137add(range, next(names, i == N - 1) + "[" + std::to_string(i) + "]", value[i]);138return add(range, std::move(names), args...);139}140141// Template specialization for SetRange142template<typename... Args>143int add(const SetRange&, std::string&& names, SetRange& value, Args&&... args) {144return add(value, (next(names), std::move(names)), args...);145}146147static void make_option(OptionsMap* options, const std::string& n, int v, const SetRange& r);148149std::vector<std::unique_ptr<EntryBase>> list;150151public:152template<typename... Args>153static int add(const std::string& names, Args&&... args) {154return instance().add(SetDefaultRange, names.substr(1, names.size() - 2),155args...); // Remove trailing parenthesis156}157static void init(OptionsMap& o) {158options = &o;159for (auto& e : instance().list)160e->init_option();161read_options();162} // Deferred, due to UCIEngine::Options access163static void read_options() {164for (auto& e : instance().list)165e->read_option();166}167168static bool update_on_last;169static OptionsMap* options;170};171172template<typename... Args>173constexpr void tune_check_args(Args&&...) {174static_assert((!std::is_fundamental_v<Args> && ...), "TUNE macro arguments wrong");175}176177// Some macro magic :-) we define a dummy int variable that the compiler initializes calling Tune::add()178#define STRINGIFY(x) #x179#define UNIQUE2(x, y) x##y180#define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__181#define TUNE(...) \182int UNIQUE(p, __LINE__) = []() -> int { \183tune_check_args(__VA_ARGS__); \184return Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__); \185}();186187#define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true188189} // namespace Stockfish190191#endif // #ifndef TUNE_H_INCLUDED192193194