X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fevaluate.cpp;h=1ae6cb3ac56f88d91ca8d56be259505d2a5f35e5;hp=bb1724a4e3bf8dd86466778925cb19ad2b2caa68;hb=23ecf3d5c6ffbcfbe45acd2afcf503929474a4db;hpb=67818ee9481ba99369fa8a8d92e5c50428fb300e diff --git a/src/evaluate.cpp b/src/evaluate.cpp index bb1724a4..1ae6cb3a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 @@ -20,15 +18,50 @@ #include #include +#include #include // For std::memset #include #include +#include #include "bitboard.h" #include "evaluate.h" #include "material.h" #include "pawns.h" #include "thread.h" +#include "uci.h" + +namespace Eval { + + bool useNNUE; + std::string eval_file_loaded="None"; + + void init_NNUE() { + + useNNUE = Options["Use NNUE"]; + std::string eval_file = std::string(Options["EvalFile"]); + if (useNNUE && eval_file_loaded != eval_file) + if (Eval::NNUE::load_eval_file(eval_file)) + eval_file_loaded = eval_file; + } + + void verify_NNUE() { + + std::string eval_file = std::string(Options["EvalFile"]); + if (useNNUE && eval_file_loaded != eval_file) + { + std::cerr << "Use of NNUE evaluation, but the file " << eval_file << " was not loaded successfully. " + << "These network evaluation parameters must be available, compatible with this version of the code. " + << "The UCI option EvalFile might need to specify the full path, including the directory/folder name, to the file." << std::endl; + std::exit(EXIT_FAILURE); + } + + if (useNNUE) + sync_cout << "info string NNUE evaluation using " << eval_file << " enabled." << sync_endl; + else + sync_cout << "info string classical evaluation enabled." << sync_endl; + } +} namespace Trace { @@ -74,8 +107,10 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold = Value(1400); + constexpr Value LazyThreshold1 = Value(1400); + constexpr Value LazyThreshold2 = Value(1300); constexpr Value SpaceThreshold = Value(12222); + constexpr Value NNUEThreshold = Value(520); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -134,6 +169,7 @@ namespace { }; // Assorted bonuses and penalties + constexpr Score BadOutpost = S( -7, 36); constexpr Score BishopOnKingRing = S( 24, 0); constexpr Score BishopPawns = S( 3, 7); constexpr Score BishopXRayPawns = S( 4, 5); @@ -145,7 +181,6 @@ namespace { constexpr Score MinorBehindPawn = S( 18, 3); constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); - constexpr Score QueenInfiltration = S( -2, 14); constexpr Score ReachableOutpost = S( 31, 22); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RookOnKingRing = S( 16, 0); @@ -308,9 +343,18 @@ namespace { if (Pt == BISHOP || Pt == KNIGHT) { - // Bonus if piece is on an outpost square or can reach one - bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them); - if (bb & s) + // Bonus if the piece is on an outpost square or can reach one + // Reduced bonus for knights (BadOutpost) if few relevant targets + bb = OutpostRanks & (attackedBy[Us][PAWN] | shift(pos.pieces(PAWN))) + & ~pe->pawn_attacks_span(Them); + Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN); + + if ( Pt == KNIGHT + && bb & s & ~CenterFiles // on a side outpost + && !(b & targets) // no relevant attacks + && (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide)))) + score += BadOutpost; + else if (bb & s) score += Outpost[Pt == BISHOP]; else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) score += ReachableOutpost; @@ -379,10 +423,6 @@ namespace { Bitboard queenPinners; if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners)) score -= WeakQueen; - - // Bonus for queen on weak square in enemy camp - if (relative_rank(Us, s) > RANK_4 && (~pe->pawn_attacks_span(Them) & s)) - score += QueenInfiltration; } } if (T) @@ -569,17 +609,21 @@ namespace { // Bonus for threats on the next moves against enemy queen if (pos.count(Them) == 1) { + bool queenImbalance = pos.count() == 1; + Square s = pos.square(Them); - safe = mobilityArea[Us] & ~stronglyProtected; + safe = mobilityArea[Us] + & ~pos.pieces(Us, PAWN) + & ~stronglyProtected; b = attackedBy[Us][KNIGHT] & attacks_bb(s); - score += KnightOnQueen * popcount(b & safe); + score += KnightOnQueen * popcount(b & safe) * (1 + queenImbalance); b = (attackedBy[Us][BISHOP] & attacks_bb(s, pos.pieces())) | (attackedBy[Us][ROOK ] & attacks_bb(s, pos.pieces())); - score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]); + score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance); } if (T) @@ -719,9 +763,9 @@ namespace { } - // Evaluation::winnable() adjusts the mg and eg score components based on the - // known attacking/defending status of the players. A single value is derived - // by interpolation from the mg and eg values and returned. + // Evaluation::winnable() adjusts the midgame and endgame score components, based on + // the known attacking/defending status of the players. The final value is derived + // by interpolation from the midgame and endgame values. template Value Evaluation::winnable(Score score) const { @@ -764,7 +808,7 @@ namespace { Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK; int sf = me->scale_factor(pos, strongSide); - // If scale is not already specific, scale down the endgame via general heuristics + // If scale factor is not already specific, scale down via general heuristics if (sf == SCALE_FACTOR_NORMAL) { if (pos.opposite_bishops()) @@ -830,9 +874,12 @@ namespace { score += pe->pawn_score(WHITE) - pe->pawn_score(BLACK); // Early exit if score is high - Value v = (mg_value(score) + eg_value(score)) / 2; - if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64) - return pos.side_to_move() == WHITE ? v : -v; + auto lazy_skip = [&](Value lazyThreshold) { + return abs(mg_value(score) + eg_value(score)) / 2 > lazyThreshold + pos.non_pawn_material() / 64; + }; + + if (lazy_skip(LazyThreshold1)) + goto make_v; // Main evaluation begins here initialize(); @@ -849,12 +896,17 @@ namespace { // More complex interactions that require fully populated attack bitboards score += king< WHITE>() - king< BLACK>() - + threats() - threats() - + passed< WHITE>() - passed< BLACK>() + + passed< WHITE>() - passed< BLACK>(); + + if (lazy_skip(LazyThreshold2)) + goto make_v; + + score += threats() - threats() + space< WHITE>() - space< BLACK>(); +make_v: // Derive single value from mg and eg parts of score - v = winnable(score); + Value v = winnable(score); // In case of tracing add all remaining individual evaluation terms if (T) @@ -884,47 +936,66 @@ namespace { /// evaluation of the position from the point of view of the side to move. Value Eval::evaluate(const Position& pos) { + + if (Eval::useNNUE) + { + Value v = eg_value(pos.psq_score()); + // Take NNUE eval only on balanced positions + if (abs(v) < NNUEThreshold) + return NNUE::evaluate(pos) + Tempo; + } return Evaluation(pos).value(); } - /// trace() is like evaluate(), but instead of returning a value, it returns /// a string (suitable for outputting to stdout) that contains the detailed /// descriptions and values of each evaluation term. Useful for debugging. +/// Trace scores are from white's point of view std::string Eval::trace(const Position& pos) { if (pos.checkers()) - return "Total evaluation: none (in check)"; - - std::memset(scores, 0, sizeof(scores)); + return "Final evaluation: none (in check)"; - pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt + std::stringstream ss; + ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); - Value v = Evaluation(pos).value(); + Value v; - v = pos.side_to_move() == WHITE ? v : -v; // Trace scores are from white's point of view + if (Eval::useNNUE) + { + v = NNUE::evaluate(pos); + } + else + { + std::memset(scores, 0, sizeof(scores)); + + pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt + + v = Evaluation(pos).value(); + + ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) + << " Term | White | Black | Total \n" + << " | MG EG | MG EG | MG EG \n" + << " ------------+-------------+-------------+------------\n" + << " Material | " << Term(MATERIAL) + << " Imbalance | " << Term(IMBALANCE) + << " Pawns | " << Term(PAWN) + << " Knights | " << Term(KNIGHT) + << " Bishops | " << Term(BISHOP) + << " Rooks | " << Term(ROOK) + << " Queens | " << Term(QUEEN) + << " Mobility | " << Term(MOBILITY) + << " King safety | " << Term(KING) + << " Threats | " << Term(THREAT) + << " Passed | " << Term(PASSED) + << " Space | " << Term(SPACE) + << " Winnable | " << Term(WINNABLE) + << " ------------+-------------+-------------+------------\n" + << " Total | " << Term(TOTAL); + } - std::stringstream ss; - ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) - << " Term | White | Black | Total \n" - << " | MG EG | MG EG | MG EG \n" - << " ------------+-------------+-------------+------------\n" - << " Material | " << Term(MATERIAL) - << " Imbalance | " << Term(IMBALANCE) - << " Pawns | " << Term(PAWN) - << " Knights | " << Term(KNIGHT) - << " Bishops | " << Term(BISHOP) - << " Rooks | " << Term(ROOK) - << " Queens | " << Term(QUEEN) - << " Mobility | " << Term(MOBILITY) - << " King safety | " << Term(KING) - << " Threats | " << Term(THREAT) - << " Passed | " << Term(PASSED) - << " Space | " << Term(SPACE) - << " Winnable | " << Term(WINNABLE) - << " ------------+-------------+-------------+------------\n" - << " Total | " << Term(TOTAL); + v = pos.side_to_move() == WHITE ? v : -v; ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n";