mirror of
https://github.com/sockspls/badfish
synced 2025-04-30 16:53:09 +00:00

These values represent the lowest Elo rating in the skill level calculation, and the highest one, but it's not clear from the code where these values come from other than the comment. This should improve code readability and maintainability. It makes the purpose of the values clear and allows for easy modification if the Elo range for skill level calculation changes in the future. Moved the Skill struct definition from search.cpp to search.h header file to define the Search::Skill struct, making it accessible from other files. closes https://github.com/official-stockfish/Stockfish/pull/5508 No functional change
330 lines
10 KiB
C++
330 lines
10 KiB
C++
/*
|
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
Copyright (C) 2004-2024 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/>.
|
|
*/
|
|
|
|
#include "engine.h"
|
|
|
|
#include <cassert>
|
|
#include <deque>
|
|
#include <iosfwd>
|
|
#include <memory>
|
|
#include <ostream>
|
|
#include <sstream>
|
|
#include <string_view>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "evaluate.h"
|
|
#include "misc.h"
|
|
#include "nnue/network.h"
|
|
#include "nnue/nnue_common.h"
|
|
#include "perft.h"
|
|
#include "position.h"
|
|
#include "search.h"
|
|
#include "syzygy/tbprobe.h"
|
|
#include "types.h"
|
|
#include "uci.h"
|
|
#include "ucioption.h"
|
|
|
|
namespace Stockfish {
|
|
|
|
namespace NN = Eval::NNUE;
|
|
|
|
constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
|
constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048;
|
|
|
|
Engine::Engine(std::string path) :
|
|
binaryDirectory(CommandLine::get_binary_directory(path)),
|
|
numaContext(NumaConfig::from_system()),
|
|
states(new std::deque<StateInfo>(1)),
|
|
threads(),
|
|
networks(
|
|
numaContext,
|
|
NN::Networks(
|
|
NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG),
|
|
NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) {
|
|
pos.set(StartFEN, false, &states->back());
|
|
capSq = SQ_NONE;
|
|
|
|
options["Debug Log File"] << Option("", [](const Option& o) {
|
|
start_logger(o);
|
|
return std::nullopt;
|
|
});
|
|
|
|
options["NumaPolicy"] << Option("auto", [this](const Option& o) {
|
|
set_numa_config_from_option(o);
|
|
return numa_config_information_as_string() + "\n" + thread_binding_information_as_string();
|
|
});
|
|
|
|
options["Threads"] << Option(1, 1, 1024, [this](const Option&) {
|
|
resize_threads();
|
|
return thread_binding_information_as_string();
|
|
});
|
|
|
|
options["Hash"] << Option(16, 1, MaxHashMB, [this](const Option& o) {
|
|
set_tt_size(o);
|
|
return std::nullopt;
|
|
});
|
|
|
|
options["Clear Hash"] << Option([this](const Option&) {
|
|
search_clear();
|
|
return std::nullopt;
|
|
});
|
|
options["Ponder"] << Option(false);
|
|
options["MultiPV"] << Option(1, 1, MAX_MOVES);
|
|
options["Skill Level"] << Option(20, 0, 20);
|
|
options["Move Overhead"] << Option(10, 0, 5000);
|
|
options["nodestime"] << Option(0, 0, 10000);
|
|
options["UCI_Chess960"] << Option(false);
|
|
options["UCI_LimitStrength"] << Option(false);
|
|
options["UCI_Elo"] << Option(Stockfish::Search::Skill::LowestElo,
|
|
Stockfish::Search::Skill::LowestElo,
|
|
Stockfish::Search::Skill::HighestElo);
|
|
options["UCI_ShowWDL"] << Option(false);
|
|
options["SyzygyPath"] << Option("", [](const Option& o) {
|
|
Tablebases::init(o);
|
|
return std::nullopt;
|
|
});
|
|
options["SyzygyProbeDepth"] << Option(1, 1, 100);
|
|
options["Syzygy50MoveRule"] << Option(true);
|
|
options["SyzygyProbeLimit"] << Option(7, 0, 7);
|
|
options["EvalFile"] << Option(EvalFileDefaultNameBig, [this](const Option& o) {
|
|
load_big_network(o);
|
|
return std::nullopt;
|
|
});
|
|
options["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, [this](const Option& o) {
|
|
load_small_network(o);
|
|
return std::nullopt;
|
|
});
|
|
|
|
load_networks();
|
|
resize_threads();
|
|
}
|
|
|
|
std::uint64_t Engine::perft(const std::string& fen, Depth depth, bool isChess960) {
|
|
verify_networks();
|
|
|
|
return Benchmark::perft(fen, depth, isChess960);
|
|
}
|
|
|
|
void Engine::go(Search::LimitsType& limits) {
|
|
assert(limits.perft == 0);
|
|
verify_networks();
|
|
limits.capSq = capSq;
|
|
|
|
threads.start_thinking(options, pos, states, limits);
|
|
}
|
|
void Engine::stop() { threads.stop = true; }
|
|
|
|
void Engine::search_clear() {
|
|
wait_for_search_finished();
|
|
|
|
tt.clear(threads);
|
|
threads.clear();
|
|
|
|
// @TODO wont work with multiple instances
|
|
Tablebases::init(options["SyzygyPath"]); // Free mapped files
|
|
}
|
|
|
|
void Engine::set_on_update_no_moves(std::function<void(const Engine::InfoShort&)>&& f) {
|
|
updateContext.onUpdateNoMoves = std::move(f);
|
|
}
|
|
|
|
void Engine::set_on_update_full(std::function<void(const Engine::InfoFull&)>&& f) {
|
|
updateContext.onUpdateFull = std::move(f);
|
|
}
|
|
|
|
void Engine::set_on_iter(std::function<void(const Engine::InfoIter&)>&& f) {
|
|
updateContext.onIter = std::move(f);
|
|
}
|
|
|
|
void Engine::set_on_bestmove(std::function<void(std::string_view, std::string_view)>&& f) {
|
|
updateContext.onBestmove = std::move(f);
|
|
}
|
|
|
|
void Engine::wait_for_search_finished() { threads.main_thread()->wait_for_search_finished(); }
|
|
|
|
void Engine::set_position(const std::string& fen, const std::vector<std::string>& moves) {
|
|
// Drop the old state and create a new one
|
|
states = StateListPtr(new std::deque<StateInfo>(1));
|
|
pos.set(fen, options["UCI_Chess960"], &states->back());
|
|
|
|
capSq = SQ_NONE;
|
|
for (const auto& move : moves)
|
|
{
|
|
auto m = UCIEngine::to_move(pos, move);
|
|
|
|
if (m == Move::none())
|
|
break;
|
|
|
|
states->emplace_back();
|
|
pos.do_move(m, states->back());
|
|
|
|
capSq = SQ_NONE;
|
|
DirtyPiece& dp = states->back().dirtyPiece;
|
|
if (dp.dirty_num > 1 && dp.to[1] == SQ_NONE)
|
|
capSq = m.to_sq();
|
|
}
|
|
}
|
|
|
|
// modifiers
|
|
|
|
void Engine::set_numa_config_from_option(const std::string& o) {
|
|
if (o == "auto" || o == "system")
|
|
{
|
|
numaContext.set_numa_config(NumaConfig::from_system());
|
|
}
|
|
else if (o == "hardware")
|
|
{
|
|
// Don't respect affinity set in the system.
|
|
numaContext.set_numa_config(NumaConfig::from_system(false));
|
|
}
|
|
else if (o == "none")
|
|
{
|
|
numaContext.set_numa_config(NumaConfig{});
|
|
}
|
|
else
|
|
{
|
|
numaContext.set_numa_config(NumaConfig::from_string(o));
|
|
}
|
|
|
|
// Force reallocation of threads in case affinities need to change.
|
|
resize_threads();
|
|
}
|
|
|
|
void Engine::resize_threads() {
|
|
threads.wait_for_search_finished();
|
|
threads.set(numaContext.get_numa_config(), {options, threads, tt, networks}, updateContext);
|
|
|
|
// Reallocate the hash with the new threadpool size
|
|
set_tt_size(options["Hash"]);
|
|
}
|
|
|
|
void Engine::set_tt_size(size_t mb) {
|
|
wait_for_search_finished();
|
|
tt.resize(mb, threads);
|
|
}
|
|
|
|
void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; }
|
|
|
|
// network related
|
|
|
|
void Engine::verify_networks() const {
|
|
networks->big.verify(options["EvalFile"]);
|
|
networks->small.verify(options["EvalFileSmall"]);
|
|
}
|
|
|
|
void Engine::load_networks() {
|
|
networks.modify_and_replicate([this](NN::Networks& networks_) {
|
|
networks_.big.load(binaryDirectory, options["EvalFile"]);
|
|
networks_.small.load(binaryDirectory, options["EvalFileSmall"]);
|
|
});
|
|
threads.clear();
|
|
}
|
|
|
|
void Engine::load_big_network(const std::string& file) {
|
|
networks.modify_and_replicate(
|
|
[this, &file](NN::Networks& networks_) { networks_.big.load(binaryDirectory, file); });
|
|
threads.clear();
|
|
}
|
|
|
|
void Engine::load_small_network(const std::string& file) {
|
|
networks.modify_and_replicate(
|
|
[this, &file](NN::Networks& networks_) { networks_.small.load(binaryDirectory, file); });
|
|
threads.clear();
|
|
}
|
|
|
|
void Engine::save_network(const std::pair<std::optional<std::string>, std::string> files[2]) {
|
|
networks.modify_and_replicate([&files](NN::Networks& networks_) {
|
|
networks_.big.save(files[0].first);
|
|
networks_.small.save(files[1].first);
|
|
});
|
|
}
|
|
|
|
// utility functions
|
|
|
|
void Engine::trace_eval() const {
|
|
StateListPtr trace_states(new std::deque<StateInfo>(1));
|
|
Position p;
|
|
p.set(pos.fen(), options["UCI_Chess960"], &trace_states->back());
|
|
|
|
verify_networks();
|
|
|
|
sync_cout << "\n" << Eval::trace(p, *networks) << sync_endl;
|
|
}
|
|
|
|
const OptionsMap& Engine::get_options() const { return options; }
|
|
OptionsMap& Engine::get_options() { return options; }
|
|
|
|
std::string Engine::fen() const { return pos.fen(); }
|
|
|
|
void Engine::flip() { pos.flip(); }
|
|
|
|
std::string Engine::visualize() const {
|
|
std::stringstream ss;
|
|
ss << pos;
|
|
return ss.str();
|
|
}
|
|
|
|
std::vector<std::pair<size_t, size_t>> Engine::get_bound_thread_count_by_numa_node() const {
|
|
auto counts = threads.get_bound_thread_count_by_numa_node();
|
|
const NumaConfig& cfg = numaContext.get_numa_config();
|
|
std::vector<std::pair<size_t, size_t>> ratios;
|
|
NumaIndex n = 0;
|
|
for (; n < counts.size(); ++n)
|
|
ratios.emplace_back(counts[n], cfg.num_cpus_in_numa_node(n));
|
|
if (!counts.empty())
|
|
for (; n < cfg.num_numa_nodes(); ++n)
|
|
ratios.emplace_back(0, cfg.num_cpus_in_numa_node(n));
|
|
return ratios;
|
|
}
|
|
|
|
std::string Engine::get_numa_config_as_string() const {
|
|
return numaContext.get_numa_config().to_string();
|
|
}
|
|
|
|
std::string Engine::numa_config_information_as_string() const {
|
|
auto cfgStr = get_numa_config_as_string();
|
|
return "Available processors: " + cfgStr;
|
|
}
|
|
|
|
std::string Engine::thread_binding_information_as_string() const {
|
|
auto boundThreadsByNode = get_bound_thread_count_by_numa_node();
|
|
std::stringstream ss;
|
|
|
|
size_t threadsSize = threads.size();
|
|
ss << "Using " << threadsSize << (threadsSize > 1 ? " threads" : " thread");
|
|
|
|
if (boundThreadsByNode.empty())
|
|
return ss.str();
|
|
|
|
ss << " with NUMA node thread binding: ";
|
|
|
|
bool isFirst = true;
|
|
|
|
for (auto&& [current, total] : boundThreadsByNode)
|
|
{
|
|
if (!isFirst)
|
|
ss << ":";
|
|
ss << current << "/" << total;
|
|
isFirst = false;
|
|
}
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
}
|