S人ving Privàte Ryän

Fixes problems with non-ASCII characters in paths. Fixes #3424.

Changes
- Bump the minimum supported Clang version to 11 to support std::filesystem
- Use std::optional for empty values instead of string sentinels like "None"
- Remove the FixedString class
- Move the EvalFile network path from the network struct to the engine
- Simplify parts of the command-line handling
- Remove the old dual-net logic from export_net
- And ofc we now support non-ascii paths for windows

Behavior Change
- Because the EvalFile is no longer part of the network, it won't be included in the hashing, thus the network sharing is no longer dependent on the EvalFile path
- As a result, if the binary version and network are the same, changing only the EvalFile location will now reuse the same shared network

This also fixes a master bug where:

```
export_net None
setoption name EvalFile value None
export_net None
Failed to export a net
```

would fail on the second export. With this patch, the second export succeeds.

Quick Fishtest Check that nets load like normal.
https://tests.stockfishchess.org/tests/view/6a455593f97ff95f787954da

<img width="833" height="1137" alt="image" src="https://github.com/user-attachments/assets/fffee63b-c63a-4d5e-b13d-87d58bc28c88" />

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

No functional change

Co-authored-by: anematode <timothy.herchen@gmail.com>
This commit is contained in:
Disservin
2026-07-06 11:30:51 +02:00
committed by Joost VandeVondele
co-authored by anematode
parent d5bbc6b67d
commit 22548cb0c0
14 changed files with 240 additions and 254 deletions
-1
View File
@@ -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" }
+18 -14
View File
@@ -20,6 +20,7 @@
#include <algorithm>
#include <cassert>
#include <filesystem>
#include <deque>
#include <iosfwd>
#include <memory>
@@ -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<std::string> path) :
binaryDirectory(path ? CommandLine::get_binary_directory(*path) : ""),
Engine::Engine(std::optional<std::filesystem::path> path) :
binaryDirectory(path ? CommandLine::get_binary_directory(*path) : std::filesystem::path{}),
numaContext(NumaConfig::from_system(DefaultNumaPolicy)),
states(new std::deque<StateInfo>(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<std::string> 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<Eval::NNUE::Network> Engine::get_default_network() const {
std::unique_ptr<Eval::NNUE::Network> Engine::get_default_network() {
auto network_ = std::make_unique<NN::Network>(NN::EvalFile{EvalFileDefaultName, "None", ""});
auto network_ = std::make_unique<NN::Network>();
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::optional<std::string>, std::string> file) {
network.modify_and_replicate([&file](NN::Network& network_) { network_.save(file.first); });
void Engine::save_network(const std::optional<std::filesystem::path>& file) {
network.modify_and_replicate(
[&file, this](NN::Network& network_) { network_.save(networkFile, file); });
}
// utility functions
+8 -5
View File
@@ -20,6 +20,7 @@
#define ENGINE_H_INCLUDED
#include <functional>
#include <filesystem>
#include <map>
#include <memory>
#include <optional>
@@ -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<std::string> path = std::nullopt);
Engine(std::optional<std::filesystem::path> 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<Eval::NNUE::Network> get_default_network() const;
void load_network(const std::string& file);
void save_network(std::pair<std::optional<std::string>, std::string> file);
std::unique_ptr<Eval::NNUE::Network> get_default_network();
void load_network(const std::filesystem::path& file);
void save_network(const std::optional<std::filesystem::path>& 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<Eval::NNUE::Network> network;
Search::SearchManager::UpdateContext updateContext;
+3 -1
View File
@@ -18,6 +18,7 @@
#include <iostream>
#include <memory>
#include <utility>
#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<UCIEngine>(argc, argv);
auto cli = CommandLine(argc, argv);
auto uci = std::make_unique<UCIEngine>(std::move(cli));
Tune::init(uci->engine_options());
+79 -40
View File
@@ -22,8 +22,10 @@
#include <atomic>
#include <cassert>
#include <cctype>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
@@ -33,10 +35,21 @@
#include <sstream>
#include <string_view>
#if defined(_WIN32)
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <direct.h>
#include <windows.h>
#include <shellapi.h>
#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 <direct.h>
#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 <unistd.h>
#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<int>(path.size());
int wlen = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), u8len, NULL, 0);
std::wstring wstr(static_cast<usize>(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
+21 -86
View File
@@ -30,10 +30,9 @@
#include <exception> // IWYU pragma: keep
// IWYU pragma: no_include <__exception/terminate.h>
#include <functional>
#include <filesystem>
#include <iosfwd>
#include <optional>
#include <cstring>
#include <memory>
#include <string>
#include <string_view>
#include <type_traits>
@@ -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<usize Capacity>
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<typename T>
bool operator==(const T& other) const noexcept {
return (std::string_view) (*this) == other;
}
template<typename T>
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<std::string> argv_storage;
std::vector<char*> argv_utf8;
#endif
};
namespace Utility {
@@ -666,13 +606,8 @@ void move_to_front(std::vector<T>& vec, Predicate pred) {
#define RESTRICT
#endif
void set_console_utf8();
} // namespace Stockfish
template<Stockfish::usize N>
struct std::hash<Stockfish::FixedString<N>> {
Stockfish::usize operator()(const Stockfish::FixedString<N>& fstr) const noexcept {
return Stockfish::hash_bytes(fstr.data(), fstr.size());
}
};
#endif // #ifndef MISC_H_INCLUDED
+45 -49
View File
@@ -24,6 +24,7 @@
#include <optional>
#include <type_traits>
#include <vector>
#include <filesystem>
#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<std::string> dirs = {"<internal>", "", rootDirectory,
stringify(DEFAULT_NNUE_DIRECTORY)};
std::vector<fs::path> dirs = {fs::path{}, rootDirectory,
fs::path(stringify(DEFAULT_NNUE_DIRECTORY))};
#else
std::vector<std::string> dirs = {"<internal>", "", rootDirectory};
std::vector<fs::path> 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 != "<internal>")
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<std::string>& 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<fs::path>& 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<void(std::string_view)>& f) const {
void Network::verify(const std::function<void(std::string_view)>& 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<char> {
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;
}
+14 -11
View File
@@ -26,6 +26,7 @@
#include <string>
#include <string_view>
#include <tuple>
#include <filesystem>
#include "../types.h"
#include "../misc.h"
@@ -49,8 +50,7 @@ using NetworkOutput = std::tuple<Value, Value>;
// 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<std::string>& filename) const;
void load(const std::filesystem::path& rootDirectory,
std::filesystem::path evalfilePath,
EvalFile& evalFile);
bool save(const EvalFile& evalFile, const std::optional<std::filesystem::path>& filename) const;
usize get_content_hash() const;
@@ -68,18 +70,21 @@ class Network {
AccumulatorCaches& cache) const;
void verify(std::string evalfilePath, const std::function<void(std::string_view)>&) const;
void verify(const std::function<void(std::string_view)>& 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<std::string> 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
+1
View File
@@ -25,6 +25,7 @@
#include <iomanip>
#include <iosfwd>
#include <iostream>
#include <memory>
#include <sstream>
#include "../position.h"
+9 -16
View File
@@ -19,12 +19,15 @@
#ifndef NNUE_MISC_H_INCLUDED
#define NNUE_MISC_H_INCLUDED
#include <memory>
#include <string>
#include <string_view>
#include <optional>
#include <filesystem>
#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<std::filesystem::path> 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::Eval::NNUE::EvalFile> {
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
+29 -21
View File
@@ -32,6 +32,7 @@
#include <optional>
#include <sstream>
#include <string_view>
#include <filesystem>
#include <sys/stat.h>
#include <type_traits>
#include <utility>
@@ -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<std::filesystem::path> 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<std::filesystem::path> 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
+2 -2
View File
@@ -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<uintptr_t>(ptr) % alignment == 0)
+10 -7
View File
@@ -26,6 +26,7 @@
#include <optional>
#include <sstream>
#include <string_view>
#include <filesystem>
#include <utility>
#include <vector>
@@ -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<std::string>& 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::optional<std::string>, std::string> file;
std::optional<std::filesystem::path> 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) {
+1 -1
View File
@@ -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();