Spaces:
Running
Running
| /* | |
| Stockfish, a UCI chess playing engine derived from Glaurung 2.1 | |
| Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file) | |
| Stockfish is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation, either version 3 of the License, or | |
| (at your option) any later version. | |
| Stockfish is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | |
| namespace Stockfish { | |
| bool CaseInsensitiveLess::operator()(const std::string& s1, const std::string& s2) const { | |
| return std::lexicographical_compare( | |
| s1.begin(), s1.end(), s2.begin(), s2.end(), | |
| [](char c1, char c2) { return std::tolower(c1) < std::tolower(c2); }); | |
| } | |
| void OptionsMap::add_info_listener(InfoListener&& message_func) { info = std::move(message_func); } | |
| void OptionsMap::setoption(std::istringstream& is) { | |
| std::string token, name, value; | |
| is >> token; // Consume the "name" token | |
| // Read the option name (can contain spaces) | |
| while (is >> token && token != "value") | |
| name += (name.empty() ? "" : " ") + token; | |
| // Read the option value (can contain spaces) | |
| while (is >> token) | |
| value += (value.empty() ? "" : " ") + token; | |
| if (options_map.count(name)) | |
| options_map[name] = value; | |
| else | |
| sync_cout << "No such option: " << name << sync_endl; | |
| } | |
| const Option& OptionsMap::operator[](const std::string& name) const { | |
| auto it = options_map.find(name); | |
| assert(it != options_map.end()); | |
| return it->second; | |
| } | |
| // 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)) | |
| { | |
| 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); } | |
| Option::Option(const OptionsMap* map) : | |
| parent(map) {} | |
| Option::Option(const char* v, OnChange f) : | |
| type("string"), | |
| min(0), | |
| max(0), | |
| on_change(std::move(f)) { | |
| defaultValue = currentValue = v; | |
| } | |
| Option::Option(bool v, OnChange f) : | |
| type("check"), | |
| min(0), | |
| max(0), | |
| on_change(std::move(f)) { | |
| defaultValue = currentValue = (v ? "true" : "false"); | |
| } | |
| Option::Option(OnChange f) : | |
| type("button"), | |
| min(0), | |
| max(0), | |
| on_change(std::move(f)) {} | |
| Option::Option(int v, int minv, int maxv, OnChange f) : | |
| type("spin"), | |
| min(minv), | |
| max(maxv), | |
| on_change(std::move(f)) { | |
| defaultValue = currentValue = std::to_string(v); | |
| } | |
| Option::Option(const char* v, const char* cur, OnChange f) : | |
| type("combo"), | |
| min(0), | |
| max(0), | |
| on_change(std::move(f)) { | |
| defaultValue = v; | |
| currentValue = cur; | |
| } | |
| Option::operator int() const { | |
| assert(type == "check" || type == "spin"); | |
| return (type == "spin" ? std::stoi(currentValue) : currentValue == "true"); | |
| } | |
| Option::operator std::string() const { | |
| assert(type == "string"); | |
| return currentValue; | |
| } | |
| bool Option::operator==(const char* s) const { | |
| assert(type == "combo"); | |
| return !CaseInsensitiveLess()(currentValue, s) && !CaseInsensitiveLess()(s, currentValue); | |
| } | |
| bool Option::operator!=(const char* s) const { return !(*this == s); } | |
| // 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 | |
| // from the user by console window, so let's check the bounds anyway. | |
| Option& Option::operator=(const std::string& v) { | |
| assert(!type.empty()); | |
| if ((type != "button" && type != "string" && v.empty()) | |
| || (type == "check" && v != "true" && v != "false") | |
| || (type == "spin" && (std::stoi(v) < min || std::stoi(v) > max))) | |
| return *this; | |
| if (type == "combo") | |
| { | |
| OptionsMap comboMap; // To have case insensitive compare | |
| std::string token; | |
| std::istringstream ss(defaultValue); | |
| while (ss >> token) | |
| comboMap.add(token, Option()); | |
| if (!comboMap.count(v) || v == "var") | |
| return *this; | |
| } | |
| if (type == "string") | |
| currentValue = v == "<empty>" ? "" : v; | |
| else if (type != "button") | |
| currentValue = v; | |
| if (on_change) | |
| { | |
| const auto ret = on_change(*this); | |
| if (ret && parent != nullptr && parent->info != nullptr) | |
| parent->info(ret); | |
| } | |
| return *this; | |
| } | |
| std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { | |
| for (size_t idx = 0; idx < om.options_map.size(); ++idx) | |
| for (const auto& it : om.options_map) | |
| if (it.second.idx == idx) | |
| { | |
| const Option& o = it.second; | |
| os << "\noption name " << it.first << " type " << o.type; | |
| if (o.type == "check" || o.type == "combo") | |
| os << " default " << o.defaultValue; | |
| else if (o.type == "string") | |
| { | |
| std::string defaultValue = o.defaultValue.empty() ? "<empty>" : o.defaultValue; | |
| os << " default " << defaultValue; | |
| } | |
| else if (o.type == "spin") | |
| os << " default " << stoi(o.defaultValue) << " min " << o.min << " max " | |
| << o.max; | |
| break; | |
| } | |
| return os; | |
| } | |
| } | |