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