X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fevaluate.cpp;h=c396e0f757569bbf1674c3fb44bd2b303b1c40d0;hb=a0e2debe3f1d14f84984a9a2c1482dc41f695548;hp=d4138cfaab5c78b5ee629dd40a19b75a5e8ce32b;hpb=ec42154ef2569a58dae2164e328d5bbffcb2aee9;p=stockfish diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d4138cfa..c396e0f7 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -33,6 +33,7 @@ #include "misc.h" #include "pawns.h" #include "thread.h" +#include "timeman.h" #include "uci.h" #include "incbin/incbin.h" @@ -54,7 +55,6 @@ using namespace std; -using namespace Stockfish::Eval::NNUE; namespace Stockfish { @@ -114,6 +114,26 @@ namespace Eval { } } + void NNUE::export_net(const std::optional& filename) { + std::string actualFilename; + if (filename.has_value()) { + actualFilename = filename.value(); + } else { + if (eval_file_loaded != EvalFileDefaultName) { + sync_cout << "Failed to export a net. A non-embedded net can only be saved if the filename is specified." << sync_endl; + return; + } + actualFilename = EvalFileDefaultName; + } + + ofstream stream(actualFilename, std::ios_base::binary); + if (save_eval(stream)) { + sync_cout << "Network saved successfully to " << actualFilename << "." << sync_endl; + } else { + sync_cout << "Failed to export a net." << sync_endl; + } + } + /// NNUE::verify() verifies that the last net used was loaded successfully void NNUE::verify() { @@ -257,11 +277,12 @@ namespace { S(0, 0), S(3, 44), S(37, 68), S(42, 60), S(0, 39), S(58, 43) }; + constexpr Value CorneredBishop = Value(50); + // Assorted bonuses and penalties constexpr Score UncontestedOutpost = S( 1, 10); constexpr Score BishopOnKingRing = S( 24, 0); constexpr Score BishopXRayPawns = S( 4, 5); - constexpr Score CorneredBishop = S( 50, 50); constexpr Score FlankAttacks = S( 8, 0); constexpr Score Hanging = S( 69, 36); constexpr Score KnightOnQueen = S( 16, 11); @@ -396,7 +417,8 @@ namespace { attackedBy[Us][Pt] = 0; - while (b1) { + while (b1) + { Square s = pop_lsb(b1); // Find attacked squares, including x-ray attacks for bishops and rooks @@ -477,9 +499,8 @@ namespace { { Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST); if (pos.piece_on(s + d) == make_piece(Us, PAWN)) - score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4 - : pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? CorneredBishop * 2 - : CorneredBishop; + score -= !pos.empty(s + d + pawn_push(Us)) ? 4 * make_score(CorneredBishop, CorneredBishop) + : 3 * make_score(CorneredBishop, CorneredBishop); } } } @@ -1038,46 +1059,43 @@ make_v: return v; } - // specifically correct for cornered bishops to fix FRC with NNUE. + + /// Fisher Random Chess: correction for cornered bishops, to fix chess960 play with NNUE + Value fix_FRC(const Position& pos) { - Value bAdjust = Value(0); + constexpr Bitboard Corners = 1ULL << SQ_A1 | 1ULL << SQ_H1 | 1ULL << SQ_A8 | 1ULL << SQ_H8; - constexpr Value p1=Value(209), p2=Value(136), p3=Value(148); + if (!(pos.pieces(BISHOP) & Corners)) + return VALUE_ZERO; - Color Us = pos.side_to_move(); - if ( (pos.pieces(Us, BISHOP) & relative_square(Us, SQ_A1)) - && (pos.pieces(Us, PAWN) & relative_square(Us, SQ_B2))) - { - bAdjust -= !pos.empty(relative_square(Us,SQ_B3)) ? p1 - : pos.piece_on(relative_square(Us,SQ_C3)) == make_piece(Us, PAWN) ? p2 - : p3; - } - if ( (pos.pieces(Us, BISHOP) & relative_square(Us, SQ_H1)) - && (pos.pieces(Us, PAWN) & relative_square(Us, SQ_G2))) - { - bAdjust -= !pos.empty(relative_square(Us,SQ_G3)) ? p1 - : pos.piece_on(relative_square(Us,SQ_F3)) == make_piece(Us, PAWN) ? p2 - : p3; - } - if ( (pos.pieces(~Us, BISHOP) & relative_square(Us, SQ_A8)) - && (pos.pieces(~Us, PAWN) & relative_square(Us, SQ_B7))) - { - bAdjust += !pos.empty(relative_square(Us,SQ_B6)) ? p1 - : pos.piece_on(relative_square(Us,SQ_C6)) == make_piece(~Us, PAWN) ? p2 - : p3; - } - if ( (pos.pieces(~Us, BISHOP) & relative_square(Us, SQ_H8)) - && (pos.pieces(~Us, PAWN) & relative_square(Us, SQ_G7))) - { - bAdjust += !pos.empty(relative_square(Us,SQ_G6)) ? p1 - : pos.piece_on(relative_square(Us,SQ_F6)) == make_piece(~Us, PAWN) ? p2 - : p3; - } - return bAdjust; + int correction = 0; + + if ( pos.piece_on(SQ_A1) == W_BISHOP + && pos.piece_on(SQ_B2) == W_PAWN) + correction += !pos.empty(SQ_B3) ? -CorneredBishop * 4 + : -CorneredBishop * 3; + + if ( pos.piece_on(SQ_H1) == W_BISHOP + && pos.piece_on(SQ_G2) == W_PAWN) + correction += !pos.empty(SQ_G3) ? -CorneredBishop * 4 + : -CorneredBishop * 3; + + if ( pos.piece_on(SQ_A8) == B_BISHOP + && pos.piece_on(SQ_B7) == B_PAWN) + correction += !pos.empty(SQ_B6) ? CorneredBishop * 4 + : CorneredBishop * 3; + + if ( pos.piece_on(SQ_H8) == B_BISHOP + && pos.piece_on(SQ_G7) == B_PAWN) + correction += !pos.empty(SQ_G6) ? CorneredBishop * 4 + : CorneredBishop * 3; + + return pos.side_to_move() == WHITE ? Value(correction) + : -Value(correction); } -} // namespace +} // namespace Eval /// evaluate() is the evaluator for the outer world. It returns a static @@ -1092,32 +1110,41 @@ Value Eval::evaluate(const Position& pos) { else { // Scale and shift NNUE for compatibility with search and classical evaluation - auto adjusted_NNUE = [&](){ - int mat = pos.non_pawn_material() + 2 * PawnValueMg * pos.count(); - Value nnueValue = NNUE::evaluate(pos) * (641 + mat / 32 - 4 * pos.rule50_count()) / 1024 + Tempo; + auto adjusted_NNUE = [&]() + { + int material = pos.non_pawn_material() + 4 * PawnValueMg * pos.count(); + int scale = 580 + + material / 32 + - 4 * pos.rule50_count(); + + Value nnue = NNUE::evaluate(pos) * scale / 1024 + Time.tempoNNUE; if (pos.is_chess960()) - nnueValue += fix_FRC(pos); + nnue += fix_FRC(pos); - return nnueValue; + return nnue; }; - // If there is PSQ imbalance use classical eval, with small probability if it is small + // If there is PSQ imbalance we use the classical eval. We also introduce + // a small probability of using the classical eval when PSQ imbalance is small. Value psq = Value(abs(eg_value(pos.psq_score()))); int r50 = 16 + pos.rule50_count(); bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB)); // Use classical evaluation for really low piece endgames. - // The most critical case is a bishop + A/H file pawn vs naked king draw. - bool strongClassical = pos.non_pawn_material() < 2 * RookValueMg && pos.count() < 2; + // One critical case is the draw for bishop + A/H file pawn vs naked king. + bool lowPieceEndgame = pos.non_pawn_material() == BishopValueMg + || (pos.non_pawn_material() < 2 * RookValueMg && pos.count() < 2); - v = classical || strongClassical ? Evaluation(pos).value() : adjusted_NNUE(); + v = classical || lowPieceEndgame ? Evaluation(pos).value() + : adjusted_NNUE(); // If the classical eval is small and imbalance large, use NNUE nevertheless. - // For the case of opposite colored bishops, switch to NNUE eval with - // small probability if the classical eval is less than the threshold. - if ( largePsq && !strongClassical + // For the case of opposite colored bishops, switch to NNUE eval with small + // probability if the classical eval is less than the threshold. + if ( largePsq + && !lowPieceEndgame && ( abs(v) * 16 < NNUEThreshold2 * r50 || ( pos.opposite_bishops() && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50