/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
+ Copyright (C) 2004-2021 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
#include "incbin/incbin.h"
-// Macro to embed the default NNUE file data in the engine binary (using incbin.h, by Dale Weiler).
+// Macro to embed the default efficiently updatable neural network (NNUE) file
+// data in the engine binary (using incbin.h, by Dale Weiler).
// This macro invocation will declare the following three variables
// const unsigned char gEmbeddedNNUEData[]; // a pointer to the embedded data
// const unsigned char *const gEmbeddedNNUEEnd; // a marker to the end
// const unsigned int gEmbeddedNNUESize; // the size of the embedded file
-// Note that this does not work in Microsof Visual Studio.
+// Note that this does not work in Microsoft Visual Studio.
#if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF)
INCBIN(EmbeddedNNUE, EvalFileDefaultName);
#else
using namespace std;
-using namespace Eval::NNUE;
+using namespace Stockfish::Eval::NNUE;
+
+namespace Stockfish {
namespace Eval {
bool useNNUE;
string eval_file_loaded = "None";
- /// NNUE::init() tries to load a nnue network at startup time, or when the engine
+ /// 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"
- /// The name of the nnue network is always retrieved from the EvalFile option.
+ /// The name of the NNUE network is always retrieved from the EvalFile option.
/// We search the given network in three locations: internally (the default
/// network may be embedded in the binary), in the active working directory and
/// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY
// 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(1, 8), S(3, 7)
+ S(3, 8), S(3, 9), S(2, 8), S(3, 8)
};
// KingProtector[knight/bishop] contains penalty for each distance unit to own king
// 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(56, 34), S(31, 23) };
+ constexpr Score Outpost[] = { S(57, 38), S(31, 24) };
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn
constexpr Score PassedRank[RANK_NB] = {
};
// Assorted bonuses and penalties
- constexpr Score BadOutpost = S( -7, 36);
+ constexpr Score UncontestedOutpost = S( 1, 10);
constexpr Score BishopOnKingRing = S( 24, 0);
constexpr Score BishopXRayPawns = S( 4, 5);
constexpr Score CorneredBishop = S( 50, 50);
score += BishopOnKingRing;
int mob = popcount(b & mobilityArea[Us]);
-
mobility[Us] += MobilityBonus[Pt - 2][mob];
if (Pt == BISHOP || Pt == KNIGHT)
{
// Bonus if the piece is on an outpost square or can reach one
- // Reduced bonus for knights (BadOutpost) if few relevant targets
+ // Bonus for knights (UncontestedOutpost) if few relevant targets
bb = OutpostRanks & (attackedBy[Us][PAWN] | shift<Down>(pos.pieces(PAWN)))
& ~pe->pawn_attacks_span(Them);
Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN);
&& bb & s & ~CenterFiles // on a side outpost
&& !(b & targets) // no relevant attacks
&& (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide))))
- score += BadOutpost;
+ score += UncontestedOutpost * popcount(pos.pieces(PAWN) & (s & QueenSide ? QueenSide : KingSide));
else if (bb & s)
score += Outpost[Pt == BISHOP];
else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
// Penalty if the piece is far from the king
score -= KingProtector[Pt == BISHOP] * distance(pos.square<KING>(Us), s);
- if (Pt == BISHOP)
+ if constexpr (Pt == BISHOP)
{
// Penalty according to the number of our pawns on the same color square as the
// bishop, bigger when the center files are blocked with pawns and smaller
}
}
- if (Pt == ROOK)
+ if constexpr (Pt == ROOK)
{
// Bonuses for rook on a (semi-)open or closed file
if (pos.is_on_semiopen_file(Us, s))
}
}
- if (Pt == QUEEN)
+ if constexpr (Pt == QUEEN)
{
// Penalty if any relative pin or discovered attack against the queen
Bitboard queenPinners;
score -= WeakQueen;
}
}
- if (T)
+ if constexpr (T)
Trace::add(Pt, Us, score);
return score;
// Penalty if king flank is under attack, potentially moving toward the king
score -= FlankAttacks * kingFlankAttack;
- if (T)
+ if constexpr (T)
Trace::add(KING, Us, score);
return score;
score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance);
}
- if (T)
+ if constexpr (T)
Trace::add(THREAT, Us, score);
return score;
score += bonus - PassedFile * edge_distance(file_of(s));
}
- if (T)
+ if constexpr (T)
Trace::add(PASSED, Us, score);
return score;
int weight = pos.count<ALL_PIECES>(Us) - 3 + std::min(pe->blocked_count(), 9);
Score score = make_score(bonus * weight * weight / 16, 0);
- if (T)
+ if constexpr (T)
Trace::add(SPACE, Us, score);
return score;
+ eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL;
v /= PHASE_MIDGAME;
- if (T)
+ if constexpr (T)
{
Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score)));
Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL));
Value v = winnable(score);
// In case of tracing add all remaining individual evaluation terms
- if (T)
+ if constexpr (T)
{
Trace::add(MATERIAL, pos.psq_score());
Trace::add(IMBALANCE, me->imbalance());
return v;
}
+ // specifically correct for cornered bishops to fix FRC with NNUE.
+ Value fix_FRC(const Position& pos) {
+
+ Value bAdjust = Value(0);
+
+ constexpr Value p1=Value(209), p2=Value(136), p3=Value(148);
+
+ 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;
+ }
+
} // namespace
{
// Scale and shift NNUE for compatibility with search and classical evaluation
auto adjusted_NNUE = [&](){
- int mat = pos.non_pawn_material() + PawnValueMg * pos.count<PAWN>();
- return NNUE::evaluate(pos) * (679 + mat / 32) / 1024 + Tempo;
+ int mat = pos.non_pawn_material() + 2 * PawnValueMg * pos.count<PAWN>();
+ Value nnueValue = NNUE::evaluate(pos) * (641 + mat / 32 - 4 * pos.rule50_count()) / 1024 + Tempo;
+
+ if (pos.is_chess960())
+ nnueValue += fix_FRC(pos);
+
+ return nnueValue;
};
// If there is PSQ imbalance use classical eval, with small probability if it is small
return ss.str();
}
+
+} // namespace Stockfish