mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-15 10:22:40 +01:00
Update to Stockfish 15
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2022 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
|
||||
@@ -61,7 +61,7 @@ namespace Stockfish {
|
||||
namespace Eval {
|
||||
|
||||
bool useNNUE;
|
||||
string eval_file_loaded = "None";
|
||||
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"
|
||||
@@ -78,6 +78,8 @@ namespace Eval {
|
||||
return;
|
||||
|
||||
string eval_file = string(Options["EvalFile"]);
|
||||
if (eval_file.empty())
|
||||
eval_file = EvalFileDefaultName;
|
||||
|
||||
#if defined(DEFAULT_NNUE_DIRECTORY)
|
||||
#define stringify2(x) #x
|
||||
@@ -88,13 +90,13 @@ namespace Eval {
|
||||
#endif
|
||||
|
||||
for (string directory : dirs)
|
||||
if (eval_file_loaded != eval_file)
|
||||
if (currentEvalFileName != eval_file)
|
||||
{
|
||||
if (directory != "<internal>")
|
||||
{
|
||||
ifstream stream(directory + eval_file, ios::binary);
|
||||
if (load_eval(eval_file, stream))
|
||||
eval_file_loaded = eval_file;
|
||||
currentEvalFileName = eval_file;
|
||||
}
|
||||
|
||||
if (directory == "<internal>" && eval_file == EvalFileDefaultName)
|
||||
@@ -106,30 +108,29 @@ namespace Eval {
|
||||
|
||||
MemoryBuffer buffer(const_cast<char*>(reinterpret_cast<const char*>(gEmbeddedNNUEData)),
|
||||
size_t(gEmbeddedNNUESize));
|
||||
(void) gEmbeddedNNUEEnd; // Silence warning on unused variable
|
||||
|
||||
istream stream(&buffer);
|
||||
if (load_eval(eval_file, stream))
|
||||
eval_file_loaded = eval_file;
|
||||
currentEvalFileName = eval_file;
|
||||
}
|
||||
}
|
||||
if (eval_file_loaded != eval_file)
|
||||
eval_file_loaded = "";
|
||||
}
|
||||
|
||||
/// NNUE::verify() verifies that the last net used was loaded successfully
|
||||
void NNUE::verify() {
|
||||
|
||||
string eval_file = string(Options["EvalFile"]);
|
||||
if (eval_file.empty())
|
||||
eval_file = EvalFileDefaultName;
|
||||
|
||||
if (useNNUE && eval_file_loaded != eval_file)
|
||||
if (useNNUE && currentEvalFileName != eval_file)
|
||||
{
|
||||
UCI::OptionsMap defaults;
|
||||
UCI::init(defaults);
|
||||
|
||||
string msg1 = "If the UCI option \"Use NNUE\" is set to true, 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/" + string(defaults["EvalFile"]);
|
||||
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.";
|
||||
|
||||
sync_cout << "info string ERROR: " << msg1 << sync_endl;
|
||||
@@ -192,17 +193,17 @@ using namespace Trace;
|
||||
namespace {
|
||||
|
||||
// Threshold for lazy and space evaluation
|
||||
constexpr Value LazyThreshold1 = Value(1565);
|
||||
constexpr Value LazyThreshold2 = Value(1102);
|
||||
constexpr Value LazyThreshold1 = Value(3631);
|
||||
constexpr Value LazyThreshold2 = Value(2084);
|
||||
constexpr Value SpaceThreshold = Value(11551);
|
||||
|
||||
// KingAttackWeights[PieceType] contains king attack weights by piece type
|
||||
constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 };
|
||||
constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 76, 46, 45, 14 };
|
||||
|
||||
// SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type,
|
||||
// higher if multiple safe checks are possible for that piece type.
|
||||
constexpr int SafeCheck[][2] = {
|
||||
{}, {}, {803, 1292}, {639, 974}, {1087, 1878}, {759, 1132}
|
||||
{}, {}, {805, 1292}, {650, 984}, {1071, 1886}, {730, 1128}
|
||||
};
|
||||
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
@@ -228,58 +229,58 @@ namespace {
|
||||
// BishopPawns[distance from edge] contains a file-dependent penalty for pawns on
|
||||
// squares of the same color as our bishop.
|
||||
constexpr Score BishopPawns[int(FILE_NB) / 2] = {
|
||||
S(3, 8), S(3, 9), S(2, 8), S(3, 8)
|
||||
S(3, 8), S(3, 9), S(2, 7), S(3, 7)
|
||||
};
|
||||
|
||||
// KingProtector[knight/bishop] contains penalty for each distance unit to own king
|
||||
constexpr Score KingProtector[] = { S(8, 9), S(6, 9) };
|
||||
constexpr Score KingProtector[] = { S(9, 9), S(7, 9) };
|
||||
|
||||
// Outpost[knight/bishop] contains bonuses for each knight or bishop occupying a
|
||||
// pawn protected square on rank 4 to 6 which is also safe from a pawn attack.
|
||||
constexpr Score Outpost[] = { S(57, 38), S(31, 24) };
|
||||
constexpr Score Outpost[] = { S(54, 34), S(31, 25) };
|
||||
|
||||
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn
|
||||
constexpr Score PassedRank[RANK_NB] = {
|
||||
S(0, 0), S(7, 27), S(16, 32), S(17, 40), S(64, 71), S(170, 174), S(278, 262)
|
||||
S(0, 0), S(2, 38), S(15, 36), S(22, 50), S(64, 81), S(166, 184), S(284, 269)
|
||||
};
|
||||
|
||||
constexpr Score RookOnClosedFile = S(10, 5);
|
||||
constexpr Score RookOnOpenFile[] = { S(19, 6), S(47, 26) };
|
||||
constexpr Score RookOnOpenFile[] = { S(18, 8), S(49, 26) };
|
||||
|
||||
// ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to
|
||||
// which piece type attacks which one. Attacks on lesser pieces which are
|
||||
// pawn-defended are not considered.
|
||||
constexpr Score ThreatByMinor[PIECE_TYPE_NB] = {
|
||||
S(0, 0), S(5, 32), S(55, 41), S(77, 56), S(89, 119), S(79, 162)
|
||||
S(0, 0), S(6, 37), S(64, 50), S(82, 57), S(103, 130), S(81, 163)
|
||||
};
|
||||
|
||||
constexpr Score ThreatByRook[PIECE_TYPE_NB] = {
|
||||
S(0, 0), S(3, 44), S(37, 68), S(42, 60), S(0, 39), S(58, 43)
|
||||
S(0, 0), S(3, 44), S(36, 71), S(44, 59), S(0, 39), S(60, 39)
|
||||
};
|
||||
|
||||
constexpr Value CorneredBishop = Value(50);
|
||||
|
||||
// Assorted bonuses and penalties
|
||||
constexpr Score UncontestedOutpost = S( 1, 10);
|
||||
constexpr Score UncontestedOutpost = S( 0, 10);
|
||||
constexpr Score BishopOnKingRing = S( 24, 0);
|
||||
constexpr Score BishopXRayPawns = S( 4, 5);
|
||||
constexpr Score FlankAttacks = S( 8, 0);
|
||||
constexpr Score Hanging = S( 69, 36);
|
||||
constexpr Score Hanging = S( 72, 40);
|
||||
constexpr Score KnightOnQueen = S( 16, 11);
|
||||
constexpr Score LongDiagonalBishop = S( 45, 0);
|
||||
constexpr Score MinorBehindPawn = S( 18, 3);
|
||||
constexpr Score PassedFile = S( 11, 8);
|
||||
constexpr Score PawnlessFlank = S( 17, 95);
|
||||
constexpr Score ReachableOutpost = S( 31, 22);
|
||||
constexpr Score RestrictedPiece = S( 7, 7);
|
||||
constexpr Score PassedFile = S( 13, 8);
|
||||
constexpr Score PawnlessFlank = S( 19, 97);
|
||||
constexpr Score ReachableOutpost = S( 33, 19);
|
||||
constexpr Score RestrictedPiece = S( 6, 7);
|
||||
constexpr Score RookOnKingRing = S( 16, 0);
|
||||
constexpr Score SliderOnQueen = S( 60, 18);
|
||||
constexpr Score ThreatByKing = S( 24, 89);
|
||||
constexpr Score SliderOnQueen = S( 62, 21);
|
||||
constexpr Score ThreatByKing = S( 24, 87);
|
||||
constexpr Score ThreatByPawnPush = S( 48, 39);
|
||||
constexpr Score ThreatBySafePawn = S(173, 94);
|
||||
constexpr Score ThreatBySafePawn = S(167, 99);
|
||||
constexpr Score TrappedRook = S( 55, 13);
|
||||
constexpr Score WeakQueenProtection = S( 14, 0);
|
||||
constexpr Score WeakQueen = S( 56, 15);
|
||||
constexpr Score WeakQueen = S( 57, 19);
|
||||
|
||||
|
||||
#undef S
|
||||
@@ -988,7 +989,9 @@ namespace {
|
||||
|
||||
// Early exit if score is high
|
||||
auto lazy_skip = [&](Value lazyThreshold) {
|
||||
return abs(mg_value(score) + eg_value(score)) / 2 > lazyThreshold + pos.non_pawn_material() / 64;
|
||||
return abs(mg_value(score) + eg_value(score)) > lazyThreshold
|
||||
+ std::abs(pos.this_thread()->bestValue) * 5 / 4
|
||||
+ pos.non_pawn_material() / 32;
|
||||
};
|
||||
|
||||
if (lazy_skip(LazyThreshold1))
|
||||
@@ -1053,26 +1056,22 @@ make_v:
|
||||
|
||||
if ( pos.piece_on(SQ_A1) == W_BISHOP
|
||||
&& pos.piece_on(SQ_B2) == W_PAWN)
|
||||
correction += !pos.empty(SQ_B3) ? -CorneredBishop * 4
|
||||
: -CorneredBishop * 3;
|
||||
correction -= CorneredBishop;
|
||||
|
||||
if ( pos.piece_on(SQ_H1) == W_BISHOP
|
||||
&& pos.piece_on(SQ_G2) == W_PAWN)
|
||||
correction += !pos.empty(SQ_G3) ? -CorneredBishop * 4
|
||||
: -CorneredBishop * 3;
|
||||
correction -= CorneredBishop;
|
||||
|
||||
if ( pos.piece_on(SQ_A8) == B_BISHOP
|
||||
&& pos.piece_on(SQ_B7) == B_PAWN)
|
||||
correction += !pos.empty(SQ_B6) ? CorneredBishop * 4
|
||||
: CorneredBishop * 3;
|
||||
correction += CorneredBishop;
|
||||
|
||||
if ( pos.piece_on(SQ_H8) == B_BISHOP
|
||||
&& pos.piece_on(SQ_G7) == B_PAWN)
|
||||
correction += !pos.empty(SQ_G6) ? CorneredBishop * 4
|
||||
: CorneredBishop * 3;
|
||||
correction += CorneredBishop;
|
||||
|
||||
return pos.side_to_move() == WHITE ? Value(correction)
|
||||
: -Value(correction);
|
||||
return pos.side_to_move() == WHITE ? Value(3 * correction)
|
||||
: -Value(3 * correction);
|
||||
}
|
||||
|
||||
} // namespace Eval
|
||||
@@ -1084,38 +1083,37 @@ make_v:
|
||||
Value Eval::evaluate(const Position& pos) {
|
||||
|
||||
Value v;
|
||||
bool useClassical = false;
|
||||
|
||||
if (!Eval::useNNUE)
|
||||
v = Evaluation<NO_TRACE>(pos).value();
|
||||
else
|
||||
// Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical,
|
||||
// but we switch to NNUE during long shuffling or with high material on the board.
|
||||
if ( !useNNUE
|
||||
|| ((pos.this_thread()->depth > 9 || pos.count<ALL_PIECES>() > 7) &&
|
||||
abs(eg_value(pos.psq_score())) * 5 > (856 + pos.non_pawn_material() / 64) * (10 + pos.rule50_count())))
|
||||
{
|
||||
// Scale and shift NNUE for compatibility with search and classical evaluation
|
||||
auto adjusted_NNUE = [&]()
|
||||
{
|
||||
int scale = 903
|
||||
+ 32 * pos.count<PAWN>()
|
||||
+ 32 * pos.non_pawn_material() / 1024;
|
||||
v = Evaluation<NO_TRACE>(pos).value(); // classical
|
||||
useClassical = abs(v) >= 297;
|
||||
}
|
||||
|
||||
Value nnue = NNUE::evaluate(pos, true) * scale / 1024;
|
||||
// If result of a classical evaluation is much lower than threshold fall back to NNUE
|
||||
if (useNNUE && !useClassical)
|
||||
{
|
||||
Value nnue = NNUE::evaluate(pos, true); // NNUE
|
||||
int scale = 1036 + 22 * pos.non_pawn_material() / 1024;
|
||||
Color stm = pos.side_to_move();
|
||||
Value optimism = pos.this_thread()->optimism[stm];
|
||||
Value psq = (stm == WHITE ? 1 : -1) * eg_value(pos.psq_score());
|
||||
int complexity = 35 * abs(nnue - psq) / 256;
|
||||
|
||||
if (pos.is_chess960())
|
||||
nnue += fix_FRC(pos);
|
||||
optimism = optimism * (44 + complexity) / 31;
|
||||
v = (nnue + optimism) * scale / 1024 - optimism;
|
||||
|
||||
return nnue;
|
||||
};
|
||||
|
||||
// If there is PSQ imbalance we use the classical eval, but we switch to
|
||||
// NNUE eval faster when shuffling or if the material on the board is high.
|
||||
int r50 = pos.rule50_count();
|
||||
Value psq = Value(abs(eg_value(pos.psq_score())));
|
||||
bool classical = psq * 5 > (750 + pos.non_pawn_material() / 64) * (5 + r50);
|
||||
|
||||
v = classical ? Evaluation<NO_TRACE>(pos).value() // classical
|
||||
: adjusted_NNUE(); // NNUE
|
||||
if (pos.is_chess960())
|
||||
v += fix_FRC(pos);
|
||||
}
|
||||
|
||||
// Damp down the evaluation linearly when shuffling
|
||||
v = v * (100 - pos.rule50_count()) / 100;
|
||||
v = v * (195 - pos.rule50_count()) / 211;
|
||||
|
||||
// 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);
|
||||
@@ -1140,7 +1138,12 @@ std::string Eval::trace(Position& pos) {
|
||||
|
||||
std::memset(scores, 0, sizeof(scores));
|
||||
|
||||
pos.this_thread()->trend = SCORE_ZERO; // Reset any dynamic contempt
|
||||
// Reset any global variable used in eval
|
||||
pos.this_thread()->depth = 0;
|
||||
pos.this_thread()->trend = SCORE_ZERO;
|
||||
pos.this_thread()->bestValue = VALUE_ZERO;
|
||||
pos.this_thread()->optimism[WHITE] = VALUE_ZERO;
|
||||
pos.this_thread()->optimism[BLACK] = VALUE_ZERO;
|
||||
|
||||
v = Evaluation<TRACE>(pos).value();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user