diff --git a/.github/workflows/avx2_compilers.yml b/.github/workflows/avx2_compilers.yml index 594eafee0..7c5f79ce6 100644 --- a/.github/workflows/avx2_compilers.yml +++ b/.github/workflows/avx2_compilers.yml @@ -22,7 +22,6 @@ jobs: - { name: gcc-15, comp: gcc, cxx: g++, image: "gcc:15" } # Using silkeh/clang for older versions - - { name: clang-10, comp: clang, cxx: clang++, image: "silkeh/clang:10", is_clang: true, ver: "10" } - { name: clang-11, comp: clang, cxx: clang++, image: "silkeh/clang:11", is_clang: true, ver: "11" } - { name: clang-12, comp: clang, cxx: clang++, image: "silkeh/clang:12", is_clang: true, ver: "12" } - { name: clang-13, comp: clang, cxx: clang++, image: "silkeh/clang:13", is_clang: true, ver: "13" } diff --git a/src/engine.cpp b/src/engine.cpp index 4bd8e000b..42a04c8a3 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -33,7 +34,6 @@ #include "misc.h" #include "nnue/network.h" #include "nnue/nnue_common.h" -#include "nnue/nnue_misc.h" #include "numa.h" #include "perft.h" #include "position.h" @@ -57,18 +57,19 @@ int MaxThreads = std::max(1024, 4 * int(get_hardware_concurrency())); // PR#6526). The user can always explicitly override this behavior. constexpr NumaAutoPolicy DefaultNumaPolicy = BundledL3Policy{32}; -Engine::Engine(std::optional path) : - binaryDirectory(path ? CommandLine::get_binary_directory(*path) : ""), +Engine::Engine(std::optional path) : + binaryDirectory(path ? CommandLine::get_binary_directory(*path) : std::filesystem::path{}), numaContext(NumaConfig::from_system(DefaultNumaPolicy)), states(new std::deque(1)), threads(), - network(numaContext, get_default_network()) { + networkFile{std::nullopt, ""}, + network(numaContext) { pos.set(StartFEN, false, &states->back()); options.add( // "Debug Log File", Option("", [](const Option& o) { - start_logger(o); + start_logger(path_from_utf8(std::string(o))); return std::nullopt; })); @@ -133,10 +134,11 @@ Engine::Engine(std::optional path) : options.add( // "EvalFile", Option(EvalFileDefaultName, [this](const Option& o) { - load_network(o); + load_network(path_from_utf8(std::string(o))); return std::nullopt; })); + network = get_default_network(); threads.clear(); threads.ensure_network_replicated(); resize_threads(); @@ -256,7 +258,8 @@ void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; } // network related void Engine::verify_network() const { - network->verify(options["EvalFile"], onVerifyNetwork); + const auto file = path_from_utf8(std::string(options["EvalFile"])); + network->verify(onVerifyNetwork, networkFile, file); auto statuses = network.get_status_and_errors(); for (usize i = 0; i < statuses.size(); ++i) @@ -289,24 +292,25 @@ void Engine::verify_network() const { } } -std::unique_ptr Engine::get_default_network() const { +std::unique_ptr Engine::get_default_network() { - auto network_ = std::make_unique(NN::EvalFile{EvalFileDefaultName, "None", ""}); + auto network_ = std::make_unique(); - network_->load(binaryDirectory, ""); + network_->load(binaryDirectory, std::filesystem::path{}, networkFile); return network_; } -void Engine::load_network(const std::string& file) { +void Engine::load_network(const std::filesystem::path& file) { network.modify_and_replicate( - [this, &file](NN::Network& network_) { network_.load(binaryDirectory, file); }); + [this, &file](NN::Network& network_) { network_.load(binaryDirectory, file, networkFile); }); threads.clear(); threads.ensure_network_replicated(); } -void Engine::save_network(const std::pair, std::string> file) { - network.modify_and_replicate([&file](NN::Network& network_) { network_.save(file.first); }); +void Engine::save_network(const std::optional& file) { + network.modify_and_replicate( + [&file, this](NN::Network& network_) { network_.save(networkFile, file); }); } // utility functions diff --git a/src/engine.h b/src/engine.h index be9226a77..e07630277 100644 --- a/src/engine.h +++ b/src/engine.h @@ -20,6 +20,7 @@ #define ENGINE_H_INCLUDED #include +#include #include #include #include @@ -31,6 +32,7 @@ #include "misc.h" #include "history.h" #include "nnue/network.h" +#include "nnue/nnue_misc.h" #include "numa.h" #include "position.h" #include "search.h" @@ -47,7 +49,7 @@ class Engine { using InfoFull = Search::InfoFull; using InfoIter = Search::InfoIteration; - Engine(std::optional path = std::nullopt); + Engine(std::optional path = std::nullopt); // Cannot be movable due to components holding backreferences to fields Engine(const Engine&) = delete; @@ -87,9 +89,9 @@ class Engine { // network related void verify_network() const; - std::unique_ptr get_default_network() const; - void load_network(const std::string& file); - void save_network(std::pair, std::string> file); + std::unique_ptr get_default_network(); + void load_network(const std::filesystem::path& file); + void save_network(const std::optional& file); // utility functions @@ -110,7 +112,7 @@ class Engine { std::string thread_binding_information_as_string() const; private: - const std::string binaryDirectory; + const std::filesystem::path binaryDirectory; NumaReplicationContext numaContext; @@ -120,6 +122,7 @@ class Engine { OptionsMap options; ThreadPool threads; TranspositionTable tt; + Eval::NNUE::EvalFile networkFile; LazyNumaReplicatedSystemWide network; Search::SearchManager::UpdateContext updateContext; diff --git a/src/main.cpp b/src/main.cpp index c386c4189..7e36b23dc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,6 +18,7 @@ #include #include +#include #include "attacks.h" #include "bitboard.h" @@ -43,7 +44,8 @@ int main(int argc, char* argv[]) { Attacks::init(); Position::init(); - auto uci = std::make_unique(argc, argv); + auto cli = CommandLine(argc, argv); + auto uci = std::make_unique(std::move(cli)); Tune::init(uci->engine_options()); diff --git a/src/misc.cpp b/src/misc.cpp index 4e7b912a0..765a035c7 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -22,8 +22,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -33,10 +35,21 @@ #include #include +#if defined(_WIN32) + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #include + #include +#endif + #include "types.h" namespace Stockfish { +namespace fs = std::filesystem; + namespace { // Version number or dev. @@ -83,7 +96,7 @@ class Logger { Tie in, out; public: - static void start(const std::string& fname) { + static void start(const fs::path& fname) { static Logger l; @@ -474,16 +487,61 @@ u64 hash_bytes(const char* data, usize size) { } // Trampoline helper to avoid moving Logger to misc.h -void start_logger(const std::string& fname) { Logger::start(fname); } - +void start_logger(const fs::path& fname) { Logger::start(fname); } +std::string utf8_from_wstring(std::wstring_view s) { #ifdef _WIN32 - #include - #define GETCWD _getcwd + if (s.empty()) + return {}; + + int size = + WideCharToMultiByte(CP_UTF8, 0, s.data(), int(s.size()), nullptr, 0, nullptr, nullptr); + if (size <= 0) + return {}; + + std::string out(size, '\0'); + WideCharToMultiByte(CP_UTF8, 0, s.data(), int(s.size()), out.data(), size, nullptr, nullptr); + return out; #else - #include - #define GETCWD getcwd + return std::string(s.begin(), s.end()); #endif +} + +fs::path path_from_utf8(const std::string& path) { +#ifdef _WIN32 + int u8len = static_cast(path.size()); + int wlen = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), u8len, NULL, 0); + + std::wstring wstr(static_cast(wlen), L'\0'); + MultiByteToWideChar(CP_UTF8, 0, path.c_str(), u8len, wstr.data(), wlen); + return {wstr}; +#else + return {path}; +#endif +} + +CommandLine::CommandLine(int _argc, char** _argv) : + argc(_argc), + argv(_argv) { +#ifdef _WIN32 + // Convert any non-ANSI characters passed on the command line to UTF-8 + int wargc = 0; + if (LPWSTR* wargv = CommandLineToArgvW(GetCommandLineW(), &wargc)) + { + for (int i = 0; i < wargc; ++i) + argv_storage.push_back(utf8_from_wstring(wargv[i])); + LocalFree(wargv); + + for (std::string& s : argv_storage) + argv_utf8.push_back(s.data()); + argv_utf8.push_back(nullptr); + + argc = wargc; + argv = argv_utf8.data(); + } +#endif +} + usize str_to_size_t(const std::string& s) { unsigned long long value = std::stoull(s); @@ -507,49 +565,30 @@ bool is_whitespace(std::string_view s) { return std::all_of(s.begin(), s.end(), [](char c) { return std::isspace(c); }); } -std::string CommandLine::get_binary_directory(std::string argv0) { - std::string pathSeparator; +fs::path CommandLine::get_binary_directory(fs::path argv0) { #ifdef _WIN32 - pathSeparator = "\\"; #ifdef _MSC_VER - // Under windows argv[0] may not have the extension. Also _get_pgmptr() had - // issues in some Windows 10 versions, so check returned values carefully. - char* pgmptr = nullptr; - if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr) - argv0 = pgmptr; + // Prefer the executable path reported by the CRT when available. + wchar_t* pgmptr = nullptr; + if (!_get_wpgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr) + argv0 = fs::path(pgmptr); #endif -#else - pathSeparator = "/"; #endif - // Extract the working directory - auto workingDirectory = CommandLine::get_working_directory(); - - // Extract the binary directory path from argv0 - auto binaryDirectory = argv0; - usize pos = binaryDirectory.find_last_of("\\/"); - if (pos == std::string::npos) - binaryDirectory = "." + pathSeparator; - else - binaryDirectory.resize(pos + 1); - - // Pattern replacement: "./" at the start of path is replaced by the working directory - if (binaryDirectory.find("." + pathSeparator) == 0) - binaryDirectory.replace(0, 1, workingDirectory); - + auto binaryDirectory = argv0.parent_path(); + if (binaryDirectory.empty()) + binaryDirectory = fs::path("."); return binaryDirectory; } -std::string CommandLine::get_working_directory() { - std::string workingDirectory = ""; - char buff[40000]; - char* cwd = GETCWD(buff, 40000); - if (cwd) - workingDirectory = cwd; +fs::path CommandLine::get_working_directory() { return std::filesystem::current_path(); } - return workingDirectory; +void set_console_utf8() { +#ifdef _WIN32 + SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); +#endif } - } // namespace Stockfish diff --git a/src/misc.h b/src/misc.h index d74d5e1bf..20c73bd43 100644 --- a/src/misc.h +++ b/src/misc.h @@ -30,10 +30,9 @@ #include // IWYU pragma: keep // IWYU pragma: no_include <__exception/terminate.h> #include +#include #include #include -#include -#include #include #include #include @@ -132,10 +131,13 @@ void prefetch(const void* addr) { } #endif -void start_logger(const std::string& fname); +void start_logger(const std::filesystem::path& fname); usize str_to_size_t(const std::string& s); +std::string utf8_from_wstring(std::wstring_view s); +std::filesystem::path path_from_utf8(const std::string& path); + #if defined(__linux__) struct PipeDeleter { @@ -524,88 +526,26 @@ inline void hash_combine(usize& seed, const T& v) { inline u64 hash_string(const std::string& sv) { return hash_bytes(sv.data(), sv.size()); } -template -class FixedString { - public: - FixedString() : - length_(0) { - data_[0] = '\0'; - } - - FixedString(const char* str) { - usize len = std::strlen(str); - if (len > Capacity) - std::terminate(); - std::memcpy(data_, str, len); - length_ = len; - data_[length_] = '\0'; - } - - FixedString(const std::string& str) { - if (str.size() > Capacity) - std::terminate(); - std::memcpy(data_, str.data(), str.size()); - length_ = str.size(); - data_[length_] = '\0'; - } - - usize size() const { return length_; } - usize capacity() const { return Capacity; } - - const char* c_str() const { return data_; } - const char* data() const { return data_; } - - char& operator[](usize i) { return data_[i]; } - - const char& operator[](usize i) const { return data_[i]; } - - FixedString& operator+=(const char* str) { - usize len = std::strlen(str); - if (length_ + len > Capacity) - std::terminate(); - std::memcpy(data_ + length_, str, len); - length_ += len; - data_[length_] = '\0'; - return *this; - } - - FixedString& operator+=(const FixedString& other) { return (*this += other.c_str()); } - - operator std::string() const { return std::string(data_, length_); } - - operator std::string_view() const { return std::string_view(data_, length_); } - - template - bool operator==(const T& other) const noexcept { - return (std::string_view) (*this) == other; - } - - template - bool operator!=(const T& other) const noexcept { - return (std::string_view) (*this) != other; - } - - void clear() { - length_ = 0; - data_[0] = '\0'; - } - - private: - char data_[Capacity + 1]; // +1 for null terminator - usize length_; -}; - struct CommandLine { public: - CommandLine(int _argc, char** _argv) : - argc(_argc), - argv(_argv) {} + CommandLine(int _argc, char** _argv); - static std::string get_binary_directory(std::string argv0); - static std::string get_working_directory(); + CommandLine(const CommandLine&) = delete; + CommandLine& operator=(const CommandLine&) = delete; + CommandLine(CommandLine&&) = default; + CommandLine& operator=(CommandLine&&) = default; + + static std::filesystem::path get_binary_directory(std::filesystem::path argv0); + static std::filesystem::path get_working_directory(); int argc; char** argv; + + private: +#ifdef _WIN32 + std::vector argv_storage; + std::vector argv_utf8; +#endif }; namespace Utility { @@ -666,13 +606,8 @@ void move_to_front(std::vector& vec, Predicate pred) { #define RESTRICT #endif +void set_console_utf8(); + } // namespace Stockfish -template -struct std::hash> { - Stockfish::usize operator()(const Stockfish::FixedString& fstr) const noexcept { - return Stockfish::hash_bytes(fstr.data(), fstr.size()); - } -}; - #endif // #ifndef MISC_H_INCLUDED diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index 20149c73c..9e939eb98 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #define INCBIN_SILENCE_BITCODE_WARNING #include "../incbin/incbin.h" @@ -58,8 +59,10 @@ const unsigned char gEmbeddedNNUEData[1] = {0x0}; const unsigned int gEmbeddedNNUESize = 1; #endif + namespace Stockfish::Eval::NNUE { +namespace fs = std::filesystem; namespace Detail { @@ -84,60 +87,56 @@ bool write_parameters(std::ostream& stream, const T& reference) { } // namespace Detail -void Network::load(const std::string& rootDirectory, std::string evalfilePath) { +void Network::load(const fs::path& rootDirectory, fs::path evalfilePath, EvalFile& evalFile) { #if defined(DEFAULT_NNUE_DIRECTORY) - std::vector dirs = {"", "", rootDirectory, - stringify(DEFAULT_NNUE_DIRECTORY)}; + std::vector dirs = {fs::path{}, rootDirectory, + fs::path(stringify(DEFAULT_NNUE_DIRECTORY))}; #else - std::vector dirs = {"", "", rootDirectory}; + std::vector dirs = {fs::path{}, rootDirectory}; #endif if (evalfilePath.empty()) evalfilePath = evalFile.defaultName; + if (evalFile.current != evalfilePath && evalfilePath == evalFile.defaultName) + load_internal(evalFile); + for (const auto& directory : dirs) { - if (std::string(evalFile.current) != evalfilePath) - { - if (directory != "") - load_user_net(directory, evalfilePath); - else if (evalfilePath == std::string(evalFile.defaultName)) - load_internal(); - } + if (evalFile.current != evalfilePath) + load_external(directory, evalfilePath, evalFile); } } - -bool Network::save(const std::optional& filename) const { - std::string actualFilename; - std::string msg; - - if (filename.has_value()) - actualFilename = filename.value(); - else +bool Network::save(const EvalFile& evalFile, const std::optional& filename) const { + if (!evalFile.current.has_value()) { - if (std::string(evalFile.current) != std::string(evalFile.defaultName)) - { - msg = "Failed to export a net. " - "A non-embedded net can only be saved if the filename is specified"; - - sync_cout << msg << sync_endl; - return false; - } - - actualFilename = evalFile.defaultName; + sync_cout << "Failed to export a net. No network file is currently loaded. " + "Please load a network file first." + << sync_endl; + return false; } + if (!filename.has_value() && evalFile.current != evalFile.defaultName) + { + sync_cout << "Failed to export a net. A non-embedded net can only be " + "saved if the filename is specified" + << sync_endl; + return false; + } + + fs::path actualFilename = filename.value_or(evalFile.defaultName); std::ofstream stream(actualFilename, std::ios_base::binary); - bool saved = save(stream, evalFile.current, evalFile.netDescription); - msg = saved ? "Network saved successfully to " + actualFilename : "Failed to export a net"; + bool saved = save(stream, evalFile.netDescription); + + sync_cout << (saved ? "Network saved successfully to " + actualFilename.string() + : "Failed to export a net") + << sync_endl; - sync_cout << msg << sync_endl; return saved; } - NetworkOutput Network::evaluate(const Position& pos, AccumulatorStack& accumulatorStack, AccumulatorCaches& cache) const { @@ -158,18 +157,20 @@ NetworkOutput Network::evaluate(const Position& pos, } -void Network::verify(std::string evalfilePath, - const std::function& f) const { +void Network::verify(const std::function& f, + const EvalFile& evalFile, + fs::path evalfilePath) const { if (evalfilePath.empty()) evalfilePath = evalFile.defaultName; - if (std::string(evalFile.current) != evalfilePath) + if (evalFile.current != evalfilePath) { if (f) { std::string msg1 = "Network evaluation parameters compatible with the engine must be available."; - std::string msg2 = "The network file " + evalfilePath + " was not loaded successfully."; + std::string msg2 = + "The network file " + evalfilePath.string() + " was not loaded successfully."; std::string msg3 = "The UCI option EvalFile might need to specify the full path, " "including the directory name, to the network file."; std::string msg4 = "The default net can be downloaded from: " @@ -189,8 +190,9 @@ void Network::verify(std::string evalfilePath, if (f) { usize size = sizeof(featureTransformer) + sizeof(NetworkArchitecture) * LayerStacks; - f("NNUE evaluation using " + evalfilePath + " (" + std::to_string(size / (1024 * 1024)) - + "MiB, (" + std::to_string(featureTransformer.InputDimensions) + ", " + f("NNUE evaluation using " + evalfilePath.string() + " (" + + std::to_string(size / (1024 * 1024)) + "MiB, (" + + std::to_string(featureTransformer.InputDimensions) + ", " + std::to_string(network[0].TransformedFeatureDimensions) + ", " + std::to_string(network[0].FC_0_OUTPUTS) + ", " + std::to_string(network[0].FC_1_OUTPUTS) + ", 1))"); @@ -225,8 +227,8 @@ NnueEvalTrace Network::trace_evaluate(const Position& pos, } -void Network::load_user_net(const std::string& dir, const std::string& evalfilePath) { - std::ifstream stream(dir + evalfilePath, std::ios::binary); +void Network::load_external(const fs::path& dir, const fs::path& evalfilePath, EvalFile& evalFile) { + std::ifstream stream(dir / evalfilePath, std::ios::binary); auto description = load(stream); if (description.has_value()) @@ -237,7 +239,7 @@ void Network::load_user_net(const std::string& dir, const std::string& evalfileP } -void Network::load_internal() { +void Network::load_internal(EvalFile& evalFile) { // C++ way to prepare a buffer for a memory stream class MemoryBuffer: public std::basic_streambuf { public: @@ -269,12 +271,7 @@ void Network::load_internal() { void Network::initialize() { initialized = true; } -bool Network::save(std::ostream& stream, - const std::string& name, - const std::string& netDescription) const { - if (name.empty() || name == "None") - return false; - +bool Network::save(std::ostream& stream, const std::string& netDescription) const { return write_parameters(stream, netDescription); } @@ -295,7 +292,6 @@ usize Network::get_content_hash() const { hash_combine(h, featureTransformer); for (auto&& layerstack : network) hash_combine(h, layerstack); - hash_combine(h, evalFile); return h; } diff --git a/src/nnue/network.h b/src/nnue/network.h index 4a17501f0..df102079c 100644 --- a/src/nnue/network.h +++ b/src/nnue/network.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "../types.h" #include "../misc.h" @@ -49,8 +50,7 @@ using NetworkOutput = std::tuple; // there is no way to run destructors. class Network { public: - Network(EvalFile file) : - evalFile(file) {} + Network() = default; Network(const Network& other) = default; Network(Network&& other) = default; @@ -58,8 +58,10 @@ class Network { Network& operator=(const Network& other) = default; Network& operator=(Network&& other) = default; - void load(const std::string& rootDirectory, std::string evalfilePath); - bool save(const std::optional& filename) const; + void load(const std::filesystem::path& rootDirectory, + std::filesystem::path evalfilePath, + EvalFile& evalFile); + bool save(const EvalFile& evalFile, const std::optional& filename) const; usize get_content_hash() const; @@ -68,18 +70,21 @@ class Network { AccumulatorCaches& cache) const; - void verify(std::string evalfilePath, const std::function&) const; + void verify(const std::function& f, + const EvalFile& evalFile, + std::filesystem::path evalfilePath) const; + NnueEvalTrace trace_evaluate(const Position& pos, AccumulatorStack& accumulatorStack, AccumulatorCaches& cache) const; - private: - void load_user_net(const std::string&, const std::string&); - void load_internal(); + void load_external(const std::filesystem::path&, const std::filesystem::path&, EvalFile&); + void load_internal(EvalFile&); + private: void initialize(); - bool save(std::ostream&, const std::string&, const std::string&) const; + bool save(std::ostream&, const std::string&) const; std::optional load(std::istream&); bool read_header(std::istream&, u32*, std::string*) const; @@ -94,8 +99,6 @@ class Network { // Evaluation function NetworkArchitecture network[LayerStacks]; - EvalFile evalFile; - bool initialized = false; // Hash value of evaluation function structure diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index 0bea37db8..ce004c482 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "../position.h" diff --git a/src/nnue/nnue_misc.h b/src/nnue/nnue_misc.h index 07036f921..44d40dfa6 100644 --- a/src/nnue/nnue_misc.h +++ b/src/nnue/nnue_misc.h @@ -19,12 +19,15 @@ #ifndef NNUE_MISC_H_INCLUDED #define NNUE_MISC_H_INCLUDED -#include #include +#include +#include +#include #include "../misc.h" #include "../types.h" #include "nnue_architecture.h" +#include "../evaluate.h" namespace Stockfish { @@ -32,15 +35,16 @@ class Position; namespace Eval::NNUE { -// EvalFile uses fixed string types because it's part of the network structure which must be trivial. +// NNUE file metadata uses fixed string types so it stays trivially copyable and cheap to move +// around between the engine and the network loader. struct EvalFile { // Default net name, will use one of the EvalFileDefaultName* macros defined // in evaluate.h - FixedString<256> defaultName; + constexpr static std::string_view defaultName = EvalFileDefaultName; // Selected net name, either via uci option or default - FixedString<256> current; + std::optional current; // Net description extracted from the net file - FixedString<256> netDescription; + std::string netDescription; }; struct NnueEvalTrace { @@ -59,15 +63,4 @@ std::string trace(Position& pos, const Network& network, AccumulatorCaches& cach } // namespace Stockfish::Eval::NNUE } // namespace Stockfish -template<> -struct std::hash { - Stockfish::usize operator()(const Stockfish::Eval::NNUE::EvalFile& evalFile) const noexcept { - Stockfish::usize h = 0; - Stockfish::hash_combine(h, evalFile.defaultName); - Stockfish::hash_combine(h, evalFile.current); - Stockfish::hash_combine(h, evalFile.netDescription); - return h; - } -}; - #endif // #ifndef NNUE_MISC_H_INCLUDED diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 21d619572..08b8999d2 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -227,7 +228,7 @@ static_assert(sizeof(LR) == 3, "LR tree entry must be 3 bytes"); // time only existence of the file is checked. class TBFile: public std::ifstream { - std::string fname; + std::filesystem::path fname; public: // Look for and open the file among the Paths directories where the .rtbw @@ -236,21 +237,13 @@ class TBFile: public std::ifstream { // // Example: // C:\tb\wdl345;C:\tb\wdl6;D:\tb\dtz345;D:\tb\dtz6 - static std::string Paths; + static std::vector Paths; TBFile(const std::string& f) { - #ifndef _WIN32 - constexpr char SepChar = ':'; - #else - constexpr char SepChar = ';'; - #endif - std::stringstream ss(Paths); - std::string path; - - while (std::getline(ss, path, SepChar)) + for (const auto& path : Paths) { - fname = path + "/" + f; + fname = path / std::filesystem::path(f); std::ifstream::open(fname); if (is_open()) return; @@ -273,7 +266,7 @@ class TBFile: public std::ifstream { if (statbuf.st_size % 64 != 16) { - std::cerr << "Corrupt tablebase file " << fname << std::endl; + std::cerr << "Corrupt tablebase file " << fname.string() << std::endl; exit(EXIT_FAILURE); } @@ -286,12 +279,12 @@ class TBFile: public std::ifstream { if (*baseAddress == MAP_FAILED) { - std::cerr << "Could not mmap() " << fname << std::endl; + std::cerr << "Could not mmap() " << fname.string() << std::endl; exit(EXIT_FAILURE); } #else // Note FILE_FLAG_RANDOM_ACCESS is only a hint to Windows and as such may get ignored. - HANDLE fd = CreateFileA(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, + HANDLE fd = CreateFileW(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr); if (fd == INVALID_HANDLE_VALUE) @@ -302,7 +295,7 @@ class TBFile: public std::ifstream { if (size_low % 64 != 16) { - std::cerr << "Corrupt tablebase file " << fname << std::endl; + std::cerr << "Corrupt tablebase file " << fname.string() << std::endl; exit(EXIT_FAILURE); } @@ -320,7 +313,7 @@ class TBFile: public std::ifstream { if (!*baseAddress) { - std::cerr << "MapViewOfFile() failed, name = " << fname + std::cerr << "MapViewOfFile() failed, name = " << fname.string() << ", error = " << GetLastError() << std::endl; exit(EXIT_FAILURE); } @@ -331,7 +324,7 @@ class TBFile: public std::ifstream { if (memcmp(data, Magics[type == WDL], 4)) { - std::cerr << "Corrupted table in file " << fname << std::endl; + std::cerr << "Corrupted table in file " << fname.string() << std::endl; unmap(*baseAddress, *mapping); return *baseAddress = nullptr, nullptr; } @@ -350,7 +343,7 @@ class TBFile: public std::ifstream { } }; -std::string TBFile::Paths; +std::vector TBFile::Paths; // struct PairsData contains low-level indexing information to access TB data. // There are 8, 4, or 2 PairsData records for each TBTable, according to the type @@ -1405,9 +1398,24 @@ void Tablebases::init(const std::string& paths) { TBTables.clear(); MaxCardinality = 0; - TBFile::Paths = paths; + TBFile::Paths.clear(); - if (paths.empty()) + if (!paths.empty()) + { + #ifndef _WIN32 + constexpr char SepChar = ':'; + #else + constexpr char SepChar = ';'; + #endif + + std::stringstream ss(paths); + std::string path; + while (std::getline(ss, path, SepChar)) + if (!path.empty()) + TBFile::Paths.emplace_back(path_from_utf8(path)); + } + + if (TBFile::Paths.empty()) return; // MapB1H1H7[] encodes a square below a1-h8 diagonal to 0..27 diff --git a/src/types.h b/src/types.h index e832b023c..b6d503fb0 100644 --- a/src/types.h +++ b/src/types.h @@ -65,8 +65,8 @@ #endif // Enforce minimum Clang version - #if defined(__clang__) && (__clang_major__ < 10) - #error "Stockfish requires Clang 10.0 or later for correct compilation" + #if defined(__clang__) && (__clang_major__ < 11) + #error "Stockfish requires Clang 11.0 or later for correct compilation" #endif #define ASSERT_ALIGNED(ptr, alignment) assert(reinterpret_cast(ptr) % alignment == 0) diff --git a/src/uci.cpp b/src/uci.cpp index 39268b054..c9941b7cf 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -63,9 +64,9 @@ void UCIEngine::print_info_string(std::string_view str) { sync_cout_end(); } -UCIEngine::UCIEngine(int argc, char** argv) : - engine(argv[0]), - cli(argc, argv) { +UCIEngine::UCIEngine(CommandLine cli_) : + engine(cli_.argc > 0 ? std::optional{path_from_utf8(cli_.argv[0])} : std::nullopt), + cli(std::move(cli_)) { engine.get_options().add_info_listener([](const std::optional& str) { if (str.has_value()) @@ -85,6 +86,7 @@ void UCIEngine::init_search_update_listeners() { } void UCIEngine::loop() { + set_console_utf8(); std::string token, cmd; for (int i = 1; i < cli.argc; ++i) @@ -151,10 +153,11 @@ void UCIEngine::loop() { sync_cout << compiler_info() << sync_endl; else if (token == "export_net") { - std::pair, std::string> file; + std::optional file; + std::string filename; - if (is >> file.second) - file.first = file.second; + if (is >> filename) + file = path_from_utf8(filename); engine.save_network(file); } @@ -171,7 +174,7 @@ void UCIEngine::loop() { sync_cout << "Unknown command: '" << cmd << "'. Type help for more information." << sync_endl; - } while (token != "quit" && cli.argc == 1); // The command-line arguments are one-shot + } while (token != "quit" && cli.argc <= 1); // The command-line arguments are one-shot } Search::LimitsType UCIEngine::parse_limits(std::istream& is) { diff --git a/src/uci.h b/src/uci.h index 6c3186ffd..50ce6b013 100644 --- a/src/uci.h +++ b/src/uci.h @@ -39,7 +39,7 @@ constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - class UCIEngine { public: - UCIEngine(int argc, char** argv); + UCIEngine(CommandLine cli); void loop();