Disallow same option being added twice

Now exits during startup.

```
./stockfish
Stockfish dev-20250202-243c7c6a by the Stockfish developers (see AUTHORS file)
x1,5,0,10,0.5,0.0020
Option: "x1" was already added!
```

i.e. prevents and helps debug this case

```cpp
int x1 = 5;

TUNE(x1);
TUNE(x1);
```

closes https://github.com/official-stockfish/Stockfish/pull/5847

No functional change
This commit is contained in:
Disservin
2025-02-02 18:05:16 +01:00
parent 8c73472ac8
commit c12dbdedd9
4 changed files with 100 additions and 71 deletions
+73 -48
View File
@@ -59,57 +59,83 @@ Engine::Engine(std::optional<std::string> path) :
NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) { NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) {
pos.set(StartFEN, false, &states->back()); pos.set(StartFEN, false, &states->back());
options["Debug Log File"] << Option("", [](const Option& o) {
start_logger(o);
return std::nullopt;
});
options["NumaPolicy"] << Option("auto", [this](const Option& o) { options.add( //
set_numa_config_from_option(o); "Debug Log File", Option("", [](const Option& o) {
return numa_config_information_as_string() + "\n" start_logger(o);
+ thread_allocation_information_as_string(); return std::nullopt;
}); }));
options["Threads"] << Option(1, 1, 1024, [this](const Option&) { options.add( //
resize_threads(); "NumaPolicy", Option("auto", [this](const Option& o) {
return thread_allocation_information_as_string(); set_numa_config_from_option(o);
}); return numa_config_information_as_string() + "\n"
+ thread_allocation_information_as_string();
}));
options["Hash"] << Option(16, 1, MaxHashMB, [this](const Option& o) { options.add( //
set_tt_size(o); "Threads", Option(1, 1, 1024, [this](const Option&) {
return std::nullopt; resize_threads();
}); return thread_allocation_information_as_string();
}));
options["Clear Hash"] << Option([this](const Option&) { options.add( //
search_clear(); "Hash", Option(16, 1, MaxHashMB, [this](const Option& o) {
return std::nullopt; set_tt_size(o);
}); return std::nullopt;
options["Ponder"] << Option(false); }));
options["MultiPV"] << Option(1, 1, MAX_MOVES);
options["Skill Level"] << Option(20, 0, 20); options.add( //
options["Move Overhead"] << Option(10, 0, 5000); "Clear Hash", Option([this](const Option&) {
options["nodestime"] << Option(0, 0, 10000); search_clear();
options["UCI_Chess960"] << Option(false); return std::nullopt;
options["UCI_LimitStrength"] << Option(false); }));
options["UCI_Elo"] << Option(Stockfish::Search::Skill::LowestElo,
Stockfish::Search::Skill::LowestElo, options.add( //
Stockfish::Search::Skill::HighestElo); "Ponder", Option(false));
options["UCI_ShowWDL"] << Option(false);
options["SyzygyPath"] << Option("", [](const Option& o) { options.add( //
Tablebases::init(o); "MultiPV", Option(1, 1, MAX_MOVES));
return std::nullopt;
}); options.add("Skill Level", Option(20, 0, 20));
options["SyzygyProbeDepth"] << Option(1, 1, 100);
options["Syzygy50MoveRule"] << Option(true); options.add("Move Overhead", Option(10, 0, 5000));
options["SyzygyProbeLimit"] << Option(7, 0, 7);
options["EvalFile"] << Option(EvalFileDefaultNameBig, [this](const Option& o) { options.add("nodestime", Option(0, 0, 10000));
load_big_network(o);
return std::nullopt; options.add("UCI_Chess960", Option(false));
});
options["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, [this](const Option& o) { options.add("UCI_LimitStrength", Option(false));
load_small_network(o);
return std::nullopt; options.add("UCI_Elo",
}); Option(Stockfish::Search::Skill::LowestElo, Stockfish::Search::Skill::LowestElo,
Stockfish::Search::Skill::HighestElo));
options.add("UCI_ShowWDL", Option(false));
options.add( //
"SyzygyPath", Option("", [](const Option& o) {
Tablebases::init(o);
return std::nullopt;
}));
options.add("SyzygyProbeDepth", Option(1, 1, 100));
options.add("Syzygy50MoveRule", Option(true));
options.add("SyzygyProbeLimit", Option(7, 0, 7));
options.add( //
"EvalFile", Option(EvalFileDefaultNameBig, [this](const Option& o) {
load_big_network(o);
return std::nullopt;
}));
options.add( //
"EvalFileSmall", Option(EvalFileDefaultNameSmall, [this](const Option& o) {
load_small_network(o);
return std::nullopt;
}));
load_networks(); load_networks();
resize_threads(); resize_threads();
@@ -340,5 +366,4 @@ std::string Engine::thread_allocation_information_as_string() const {
return ss.str(); return ss.str();
} }
} }
+1 -1
View File
@@ -55,7 +55,7 @@ void Tune::make_option(OptionsMap* opts, const string& n, int v, const SetRange&
if (TuneResults.count(n)) if (TuneResults.count(n))
v = TuneResults[n]; v = TuneResults[n];
(*opts)[n] << Option(v, r(v).first, r(v).second, on_tune); opts->add(n, Option(v, r(v).first, r(v).second, on_tune));
LastOption = &((*opts)[n]); LastOption = &((*opts)[n]);
// Print formatted parameters, ready to be copy-pasted in Fishtest // Print formatted parameters, ready to be copy-pasted in Fishtest
+21 -19
View File
@@ -21,6 +21,7 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cctype> #include <cctype>
#include <cstdlib>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <utility> #include <utility>
@@ -57,17 +58,31 @@ void OptionsMap::setoption(std::istringstream& is) {
sync_cout << "No such option: " << name << sync_endl; sync_cout << "No such option: " << name << sync_endl;
} }
Option OptionsMap::operator[](const std::string& name) const { const Option& OptionsMap::operator[](const std::string& name) const {
auto it = options_map.find(name); auto it = options_map.find(name);
return it != options_map.end() ? it->second : Option(this); assert(it != options_map.end());
return it->second;
} }
Option& OptionsMap::operator[](const std::string& name) { // Inits options and assigns idx in the correct printing order
void OptionsMap::add(const std::string& name, const Option& option) {
if (!options_map.count(name)) if (!options_map.count(name))
options_map[name] = Option(this); {
return options_map[name]; static size_t insert_order = 0;
options_map[name] = option;
options_map[name].parent = this;
options_map[name].idx = insert_order++;
}
else
{
std::cerr << "Option \"" << name << "\" was already added!" << std::endl;
std::exit(EXIT_FAILURE);
}
} }
std::size_t OptionsMap::count(const std::string& name) const { return options_map.count(name); } std::size_t OptionsMap::count(const std::string& name) const { return options_map.count(name); }
Option::Option(const OptionsMap* map) : Option::Option(const OptionsMap* map) :
@@ -130,19 +145,6 @@ bool Option::operator==(const char* s) const {
bool Option::operator!=(const char* s) const { return !(*this == s); } bool Option::operator!=(const char* s) const { return !(*this == s); }
// Inits options and assigns idx in the correct printing order
void Option::operator<<(const Option& o) {
static size_t insert_order = 0;
auto p = this->parent;
*this = o;
this->parent = p;
idx = insert_order++;
}
// Updates currentValue and triggers on_change() action. It's up to // Updates currentValue and triggers on_change() action. It's up to
// the GUI to check for option's limits, but we could receive the new value // the GUI to check for option's limits, but we could receive the new value
// from the user by console window, so let's check the bounds anyway. // from the user by console window, so let's check the bounds anyway.
@@ -161,7 +163,7 @@ Option& Option::operator=(const std::string& v) {
std::string token; std::string token;
std::istringstream ss(defaultValue); std::istringstream ss(defaultValue);
while (ss >> token) while (ss >> token)
comboMap[token] << Option(); comboMap.add(token, Option());
if (!comboMap.count(v) || v == "var") if (!comboMap.count(v) || v == "var")
return *this; return *this;
} }
+5 -3
View File
@@ -54,12 +54,13 @@ class Option {
friend std::ostream& operator<<(std::ostream&, const OptionsMap&); friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
int operator<<(const Option&) = delete;
private: private:
friend class OptionsMap; friend class OptionsMap;
friend class Engine; friend class Engine;
friend class Tune; friend class Tune;
void operator<<(const Option&);
std::string defaultValue, currentValue, type; std::string defaultValue, currentValue, type;
int min, max; int min, max;
@@ -82,8 +83,9 @@ class OptionsMap {
void setoption(std::istringstream&); void setoption(std::istringstream&);
Option operator[](const std::string&) const; const Option& operator[](const std::string&) const;
Option& operator[](const std::string&);
void add(const std::string&, const Option& option);
std::size_t count(const std::string&) const; std::size_t count(const std::string&) const;