X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fevaluate.cpp;h=bf6dd69a950282588c904d5f60b0305f9eb9b319;hb=6cf8d938c5950ddedb8a92cdea4712f7d507c614;hp=415c18c557395cad0bd7b1bc2c5956a5daa21679;hpb=7f1333ccf89c715933ead72e31233510c6f4d146;p=stockfish diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 415c18c5..bf6dd69a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 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 @@ -36,7 +36,7 @@ #include "timeman.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). @@ -82,20 +82,18 @@ namespace Eval { eval_file = EvalFileDefaultName; #if defined(DEFAULT_NNUE_DIRECTORY) - #define stringify2(x) #x - #define stringify(x) stringify2(x) vector dirs = { "" , "" , CommandLine::binaryDirectory , stringify(DEFAULT_NNUE_DIRECTORY) }; #else vector dirs = { "" , "" , CommandLine::binaryDirectory }; #endif - for (string directory : dirs) + for (const string& directory : dirs) if (currentEvalFileName != eval_file) { if (directory != "") { ifstream stream(directory + eval_file, ios::binary); - if (load_eval(eval_file, stream)) + if (NNUE::load_eval(eval_file, stream)) currentEvalFileName = eval_file; } @@ -111,7 +109,7 @@ namespace Eval { (void) gEmbeddedNNUEEnd; // Silence warning on unused variable istream stream(&buffer); - if (load_eval(eval_file, stream)) + if (NNUE::load_eval(eval_file, stream)) currentEvalFileName = eval_file; } } @@ -159,24 +157,24 @@ namespace Trace { Score scores[TERM_NB][COLOR_NB]; - double to_cp(Value v) { return double(v) / PawnValueEg; } + static double to_cp(Value v) { return double(v) / UCI::NormalizeToPawnValue; } - void add(int idx, Color c, Score s) { + static void add(int idx, Color c, Score s) { scores[idx][c] = s; } - void add(int idx, Score w, Score b = SCORE_ZERO) { + static void add(int idx, Score w, Score b = SCORE_ZERO) { scores[idx][WHITE] = w; scores[idx][BLACK] = b; } - std::ostream& operator<<(std::ostream& os, Score s) { + static std::ostream& operator<<(std::ostream& os, Score s) { os << std::setw(5) << to_cp(mg_value(s)) << " " << std::setw(5) << to_cp(eg_value(s)); return os; } - std::ostream& operator<<(std::ostream& os, Term t) { + static std::ostream& operator<<(std::ostream& os, Term t) { if (t == MATERIAL || t == IMBALANCE || t == WINNABLE || t == TOTAL) os << " ---- ----" << " | " << " ---- ----"; @@ -193,8 +191,8 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold1 = Value(3631); - constexpr Value LazyThreshold2 = Value(2084); + constexpr Value LazyThreshold1 = Value(3622); + constexpr Value LazyThreshold2 = Value(1962); constexpr Value SpaceThreshold = Value(11551); // KingAttackWeights[PieceType] contains king attack weights by piece type @@ -388,10 +386,10 @@ namespace { template template Score Evaluation::pieces() { - constexpr Color Them = ~Us; - constexpr Direction Down = -pawn_push(Us); - constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB - : Rank5BB | Rank4BB | Rank3BB); + constexpr Color Them = ~Us; + [[maybe_unused]] constexpr Direction Down = -pawn_push(Us); + [[maybe_unused]] constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB + : Rank5BB | Rank4BB | Rank3BB); Bitboard b1 = pos.pieces(Us, Pt); Bitboard b, bb; Score score = SCORE_ZERO; @@ -430,7 +428,7 @@ namespace { int mob = popcount(b & mobilityArea[Us]); mobility[Us] += MobilityBonus[Pt - 2][mob]; - if (Pt == BISHOP || Pt == KNIGHT) + if constexpr (Pt == BISHOP || Pt == KNIGHT) { // Bonus if the piece is on an outpost square or can reach one // Bonus for knights (UncontestedOutpost) if few relevant targets @@ -981,7 +979,7 @@ namespace { // Initialize score by reading the incrementally updated scores included in // the position object (material + piece square tables) and the material // imbalance. Score is computed internally from the white point of view. - Score score = pos.psq_score() + me->imbalance() + pos.this_thread()->trend; + Score score = pos.psq_score() + me->imbalance(); // Probe the pawn hash table pe = Pawns::probe(pos); @@ -1042,38 +1040,6 @@ make_v: return v; } - - /// Fisher Random Chess: correction for cornered bishops, to fix chess960 play with NNUE - - Value fix_FRC(const Position& pos) { - - constexpr Bitboard Corners = 1ULL << SQ_A1 | 1ULL << SQ_H1 | 1ULL << SQ_A8 | 1ULL << SQ_H8; - - if (!(pos.pieces(BISHOP) & Corners)) - return VALUE_ZERO; - - int correction = 0; - - if ( pos.piece_on(SQ_A1) == W_BISHOP - && pos.piece_on(SQ_B2) == W_PAWN) - correction -= CorneredBishop; - - if ( pos.piece_on(SQ_H1) == W_BISHOP - && pos.piece_on(SQ_G2) == W_PAWN) - correction -= CorneredBishop; - - if ( pos.piece_on(SQ_A8) == B_BISHOP - && pos.piece_on(SQ_B7) == B_PAWN) - correction += CorneredBishop; - - if ( pos.piece_on(SQ_H8) == B_BISHOP - && pos.piece_on(SQ_G7) == B_PAWN) - correction += CorneredBishop; - - return pos.side_to_move() == WHITE ? Value(3 * correction) - : -Value(3 * correction); - } - } // namespace Eval @@ -1082,40 +1048,35 @@ make_v: Value Eval::evaluate(const Position& pos) { + assert(!pos.checkers()); + Value v; - // 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. - bool useClassical = (pos.this_thread()->depth > 9 || pos.count() > 7) && - abs(eg_value(pos.psq_score())) * 5 > (856 + pos.non_pawn_material() / 64) * (10 + pos.rule50_count()); - - // 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 || useClassical) - { - v = Evaluation(pos).value(); // classical - useClassical = abs(v) >= 297; - } + Value psq = pos.psq_eg_stm(); + + // We use the much less accurate but faster Classical eval when the NNUE + // option is set to false. Otherwise we use the NNUE eval unless the + // PSQ advantage is decisive. (~4 Elo at STC, 1 Elo at LTC) + bool useClassical = !useNNUE || abs(psq) > 2048; - // If result of a classical evaluation is much lower than threshold fall back to NNUE - if (useNNUE && !useClassical) + if (useClassical) + v = Evaluation(pos).value(); + else { - int complexity; - int scale = 1048 + 109 * pos.non_pawn_material() / 5120; - Color stm = pos.side_to_move(); - Value optimism = pos.this_thread()->optimism[stm]; - Value psq = (stm == WHITE ? 1 : -1) * eg_value(pos.psq_score()); - Value nnue = NNUE::evaluate(pos, true, &complexity); // NNUE - - complexity = (137 * complexity + 137 * abs(nnue - psq)) / 256; - optimism = optimism * (255 + complexity) / 256; - v = (nnue * scale + optimism * (scale - 848)) / 1024; - - if (pos.is_chess960()) - v += fix_FRC(pos); + int nnueComplexity; + int npm = pos.non_pawn_material() / 64; + + Color stm = pos.side_to_move(); + Value optimism = pos.this_thread()->optimism[stm]; + + Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); + + // Blend optimism with nnue complexity and (semi)classical complexity + optimism += 25 * optimism * (nnueComplexity + abs(psq - nnue)) / 16384; + v = (nnue * (945 + npm) + optimism * (174 + npm)) / 1024; } // Damp down the evaluation linearly when shuffling - v = v * (195 - pos.rule50_count()) / 211; + v = v * (200 - pos.rule50_count()) / 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); @@ -1141,8 +1102,6 @@ std::string Eval::trace(Position& pos) { std::memset(scores, 0, sizeof(scores)); // 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;