X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fevaluate.cpp;h=208e3ed5ba4d1c43eb555b1a2d090e6b1ad8325f;hb=9739ed7a97c153c3223b608b24717edbf2dfd7bc;hp=2ab4fa404e094c535166484677da6c5066d13a51;hpb=af110e02ec96cdb46cf84c68252a1da15a902395;p=stockfish diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 2ab4fa40..208e3ed5 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -16,23 +16,24 @@ along with this program. If not, see . */ +#include "evaluate.h" + #include #include +#include #include #include -#include #include -#include +#include #include -#include "bitboard.h" -#include "evaluate.h" +#include "incbin/incbin.h" #include "misc.h" +#include "nnue/evaluate_nnue.h" +#include "position.h" #include "thread.h" -#include "timeman.h" +#include "types.h" #include "uci.h" -#include "incbin/incbin.h" -#include "nnue/evaluate_nnue.h" // Macro to embed the default efficiently updatable neural network (NNUE) file // data in the engine binary (using incbin.h, by Dale Weiler). @@ -50,15 +51,11 @@ #endif -using namespace std; - namespace Stockfish { namespace Eval { - string currentEvalFileName = "None"; - - static double to_cp(Value v) { return double(v) / UCI::NormalizeToPawnValue; } + std::string currentEvalFileName = "None"; /// NNUE::init() tries to load a NNUE network at startup time, or when the engine /// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" @@ -70,22 +67,22 @@ namespace Eval { void NNUE::init() { - string eval_file = string(Options["EvalFile"]); + std::string eval_file = std::string(Options["EvalFile"]); if (eval_file.empty()) eval_file = EvalFileDefaultName; #if defined(DEFAULT_NNUE_DIRECTORY) - vector dirs = { "" , "" , CommandLine::binaryDirectory , stringify(DEFAULT_NNUE_DIRECTORY) }; + std::vector dirs = { "" , "" , CommandLine::binaryDirectory , stringify(DEFAULT_NNUE_DIRECTORY) }; #else - vector dirs = { "" , "" , CommandLine::binaryDirectory }; + std::vector dirs = { "" , "" , CommandLine::binaryDirectory }; #endif - for (const string& directory : dirs) + for (const std::string& directory : dirs) if (currentEvalFileName != eval_file) { if (directory != "") { - ifstream stream(directory + eval_file, ios::binary); + std::ifstream stream(directory + eval_file, std::ios::binary); if (NNUE::load_eval(eval_file, stream)) currentEvalFileName = eval_file; } @@ -93,7 +90,7 @@ namespace Eval { if (directory == "" && eval_file == EvalFileDefaultName) { // C++ way to prepare a buffer for a memory stream - class MemoryBuffer : public basic_streambuf { + class MemoryBuffer : public std::basic_streambuf { public: MemoryBuffer(char* p, size_t n) { setg(p, p, p + n); setp(p, p + n); } }; @@ -101,7 +98,7 @@ namespace Eval { size_t(gEmbeddedNNUESize)); (void) gEmbeddedNNUEEnd; // Silence warning on unused variable - istream stream(&buffer); + std::istream stream(&buffer); if (NNUE::load_eval(eval_file, stream)) currentEvalFileName = eval_file; } @@ -111,18 +108,18 @@ namespace Eval { /// NNUE::verify() verifies that the last net used was loaded successfully void NNUE::verify() { - string eval_file = string(Options["EvalFile"]); + std::string eval_file = std::string(Options["EvalFile"]); if (eval_file.empty()) eval_file = EvalFileDefaultName; if (currentEvalFileName != eval_file) { - string msg1 = "Network evaluation parameters compatible with the engine must be available."; - string msg2 = "The option is set to true, but the network file " + eval_file + " was not loaded successfully."; - string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file."; - string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + std::string(EvalFileDefaultName); - string msg5 = "The engine will be terminated now."; + std::string msg1 = "Network evaluation parameters compatible with the engine must be available."; + std::string msg2 = "The network file " + eval_file + " 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: https://tests.stockfishchess.org/api/nn/" + std::string(EvalFileDefaultName); + std::string msg5 = "The engine will be terminated now."; sync_cout << "info string ERROR: " << msg1 << sync_endl; sync_cout << "info string ERROR: " << msg2 << sync_endl; @@ -137,30 +134,54 @@ namespace Eval { } } -/// evaluate() is the evaluator for the outer world. It returns a static -/// evaluation of the position from the point of view of the side to move. -Value Eval::evaluate(const Position& pos) { +/// simple_eval() returns a static, purely materialistic evaluation of the position +/// from the point of view of the given color. It can be divided by PawnValue to get +/// an approximation of the material advantage on the board in terms of pawns. - assert(!pos.checkers()); +Value Eval::simple_eval(const Position& pos, Color c) { + return PawnValue * (pos.count(c) - pos.count(~c)) + + (pos.non_pawn_material(c) - pos.non_pawn_material(~c)); +} - Value v; - Value psq = pos.psq_eg_stm(); - int nnueComplexity; - int npm = pos.non_pawn_material() / 64; +/// evaluate() is the evaluator for the outer world. It returns a static evaluation +/// of the position from the point of view of the side to move. - Color stm = pos.side_to_move(); - Value optimism = pos.this_thread()->optimism[stm]; +Value Eval::evaluate(const Position& pos) { - Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); + assert(!pos.checkers()); - // Blend optimism with nnue complexity and (semi)classical complexity - optimism += optimism * (nnueComplexity + abs(psq - nnue)) / 512; - v = (nnue * (945 + npm) + optimism * (150 + npm)) / 1024; + Value v; + Color stm = pos.side_to_move(); + int shuffling = pos.rule50_count(); + int simpleEval = simple_eval(pos, stm) + (int(pos.key() & 7) - 3); + + bool lazy = abs(simpleEval) >= RookValue + KnightValue + + 16 * shuffling * shuffling + + abs(pos.this_thread()->bestValue) + + abs(pos.this_thread()->rootSimpleEval); + + if (lazy) + v = Value(simpleEval); + else + { + int nnueComplexity; + Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); + + Value optimism = pos.this_thread()->optimism[stm]; + + // Blend optimism and eval with nnue complexity and material imbalance + optimism += optimism * (nnueComplexity + abs(simpleEval - nnue)) / 512; + nnue -= nnue * (nnueComplexity + abs(simpleEval - nnue)) / 32768; + + int npm = pos.non_pawn_material() / 64; + v = ( nnue * (915 + npm + 9 * pos.count()) + + optimism * (154 + npm )) / 1024; + } // Damp down the evaluation linearly when shuffling - v = v * (200 - pos.rule50_count()) / 214; + v = v * (200 - shuffling) / 214; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); @@ -180,6 +201,7 @@ std::string Eval::trace(Position& pos) { // Reset any global variable used in eval pos.this_thread()->bestValue = VALUE_ZERO; + pos.this_thread()->rootSimpleEval = VALUE_ZERO; pos.this_thread()->optimism[WHITE] = VALUE_ZERO; pos.this_thread()->optimism[BLACK] = VALUE_ZERO; @@ -192,11 +214,11 @@ std::string Eval::trace(Position& pos) { Value v; v = NNUE::evaluate(pos, false); v = pos.side_to_move() == WHITE ? v : -v; - ss << "NNUE evaluation " << to_cp(v) << " (white side)\n"; + ss << "NNUE evaluation " << 0.01 * UCI::to_cp(v) << " (white side)\n"; v = evaluate(pos); v = pos.side_to_move() == WHITE ? v : -v; - ss << "Final evaluation " << to_cp(v) << " (white side)"; + ss << "Final evaluation " << 0.01 * UCI::to_cp(v) << " (white side)"; ss << " [with scaled NNUE, ...]"; ss << "\n";