Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- constexpr Value LazyThreshold1 = Value(1400);
- constexpr Value LazyThreshold2 = Value(1300);
- constexpr Value SpaceThreshold = Value(12222);
- constexpr Value NNUEThreshold1 = Value(550);
- constexpr Value NNUEThreshold2 = Value(150);
+ constexpr Value LazyThreshold1 = Value(1565);
+ constexpr Value LazyThreshold2 = Value(1102);
+ constexpr Value SpaceThreshold = Value(11551);
+ constexpr Value NNUEThreshold1 = Value(682);
+ constexpr Value NNUEThreshold2 = Value(176);
// KingAttackWeights[PieceType] contains king attack weights by piece type
constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 };
// KingAttackWeights[PieceType] contains king attack weights by piece type
constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 };
{ S(-47,-59), S(-20,-25), S( 14, -8), S( 29, 12), S( 39, 21), S( 53, 40), // Bishop
S( 53, 56), S( 60, 58), S( 62, 65), S( 69, 72), S( 78, 78), S( 83, 87),
S( 91, 88), S( 96, 98) },
{ S(-47,-59), S(-20,-25), S( 14, -8), S( 29, 12), S( 39, 21), S( 53, 40), // Bishop
S( 53, 56), S( 60, 58), S( 62, 65), S( 69, 72), S( 78, 78), S( 83, 87),
S( 91, 88), S( 96, 98) },
- { S(-61,-82), S(-20,-17), S( 2, 23) ,S( 3, 40), S( 4, 72), S( 11,100), // Rook
- S( 22,104), S( 31,120), S( 39,134), S(40 ,138), S( 41,158), S( 47,163),
- S( 59,168), S( 60,169), S( 64,173) },
+ { S(-60,-82), S(-24,-15), S( 0, 17) ,S( 3, 43), S( 4, 72), S( 14,100), // Rook
+ S( 20,102), S( 30,122), S( 41,133), S(41 ,139), S( 41,153), S( 45,160),
+ S( 57,165), S( 58,170), S( 67,175) },
{ S(-29,-49), S(-16,-29), S( -8, -8), S( -8, 17), S( 18, 39), S( 25, 54), // Queen
S( 23, 59), S( 37, 73), S( 41, 76), S( 54, 95), S( 65, 95) ,S( 68,101),
S( 69,124), S( 70,128), S( 70,132), S( 70,133) ,S( 71,136), S( 72,140),
{ S(-29,-49), S(-16,-29), S( -8, -8), S( -8, 17), S( 18, 39), S( 25, 54), // Queen
S( 23, 59), S( 37, 73), S( 41, 76), S( 54, 95), S( 65, 95) ,S( 68,101),
S( 69,124), S( 70,128), S( 70,132), S( 70,133) ,S( 71,136), S( 72,140),
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn
constexpr Score PassedRank[RANK_NB] = {
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn
constexpr Score PassedRank[RANK_NB] = {
- S(0, 0), S(9, 28), S(15, 31), S(17, 39), S(64, 70), S(171, 177), S(277, 260)
+ S(0, 0), S(7, 27), S(16, 32), S(17, 40), S(64, 71), S(170, 174), S(278, 262)
- // RookOnFile[semiopen/open] contains bonuses for each rook when there is
- // no (friendly) pawn on the rook file.
- constexpr Score RookOnFile[] = { S(19, 7), S(48, 27) };
+ constexpr Score RookOnClosedFile = S(10, 5);
+ constexpr Score RookOnOpenFile[] = { S(19, 6), S(47, 26) };
// ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to
// which piece type attacks which one. Attacks on lesser pieces which are
// ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to
// which piece type attacks which one. Attacks on lesser pieces which are
constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
: Rank5BB | Rank4BB | Rank3BB);
constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
: Rank5BB | Rank4BB | Rank3BB);
// Find attacked squares, including x-ray attacks for bishops and rooks
b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN))
: Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK))
// Find attacked squares, including x-ray attacks for bishops and rooks
b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN))
: Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK))
- File kf = file_of(pos.square<KING>(Us));
- if ((kf < FILE_E) == (file_of(s) < kf))
- score -= TrappedRook * (1 + !pos.castling_rights(Us));
+ score += RookOnOpenFile[pos.is_on_semiopen_file(Them, s)];
+ }
+ else
+ {
+ // If our pawn on this file is blocked, increase penalty
+ if ( pos.pieces(Us, PAWN)
+ & shift<Down>(pos.pieces())
+ & file_bb(s))
+ {
+ score -= RookOnClosedFile;
+ }
+
+ // Penalty when trapped by the king, even more if the king cannot castle
+ if (mob <= 3)
+ {
+ File kf = file_of(pos.square<KING>(Us));
+ if ((kf < FILE_E) == (file_of(s) < kf))
+ score -= TrappedRook * (1 + !pos.castling_rights(Us));
+ }
- kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
- + 185 * popcount(kingRing[Us] & weak)
- + 148 * popcount(unsafeChecks)
- + 98 * popcount(pos.blockers_for_king(Us))
- + 69 * kingAttacksCount[Them]
- + 3 * kingFlankAttack * kingFlankAttack / 8
- + mg_value(mobility[Them] - mobility[Us])
- - 873 * !pos.count<QUEEN>(Them)
- - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
- - 6 * mg_value(score) / 8
- - 4 * kingFlankDefense
- + 37;
+ kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] // (~10 Elo)
+ + 183 * popcount(kingRing[Us] & weak) // (~15 Elo)
+ + 148 * popcount(unsafeChecks) // (~4 Elo)
+ + 98 * popcount(pos.blockers_for_king(Us)) // (~2 Elo)
+ + 69 * kingAttacksCount[Them] // (~0.5 Elo)
+ + 3 * kingFlankAttack * kingFlankAttack / 8 // (~0.5 Elo)
+ + mg_value(mobility[Them] - mobility[Us]) // (~0.5 Elo)
+ - 873 * !pos.count<QUEEN>(Them) // (~24 Elo)
+ - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) // (~5 Elo)
+ - 6 * mg_value(score) / 8 // (~8 Elo)
+ - 4 * kingFlankDefense // (~5 Elo)
+ + 37; // (~0.5 Elo)
bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN);
if (!(pos.pieces(Them) & bb))
bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN);
if (!(pos.pieces(Them) & bb))
- // If there are no enemy attacks on passed pawn span, assign a big bonus.
+ // If there are no enemy pieces or attacks on passed pawn span, assign a big bonus.
+ // Or if there is some, but they are all attacked by our pawns, assign a bit smaller bonus.
// Otherwise assign a smaller bonus if the path to queen is not attacked
// and even smaller bonus if it is attacked but block square is not.
// Otherwise assign a smaller bonus if the path to queen is not attacked
// and even smaller bonus if it is attacked but block square is not.
- int k = !unsafeSquares ? 35 :
- !(unsafeSquares & squaresToQueen) ? 20 :
- !(unsafeSquares & blockSq) ? 9 :
+ int k = !unsafeSquares ? 36 :
+ !(unsafeSquares & ~attackedBy[Us][PAWN]) ? 30 :
+ !(unsafeSquares & squaresToQueen) ? 17 :
+ !(unsafeSquares & blockSq) ? 7 :
int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]);
int weight = pos.count<ALL_PIECES>(Us) - 3 + std::min(pe->blocked_count(), 9);
Score score = make_score(bonus * weight * weight / 16, 0);
int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]);
int weight = pos.count<ALL_PIECES>(Us) - 3 + std::min(pe->blocked_count(), 9);
Score score = make_score(bonus * weight * weight / 16, 0);
Value Evaluation<T>::winnable(Score score) const {
int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
Value Evaluation<T>::winnable(Score score) const {
int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
if ( pos.non_pawn_material(WHITE) == BishopValueMg
&& pos.non_pawn_material(BLACK) == BishopValueMg)
sf = 18 + 4 * popcount(pe->passed_pawns(strongSide));
if ( pos.non_pawn_material(WHITE) == BishopValueMg
&& pos.non_pawn_material(BLACK) == BishopValueMg)
sf = 18 + 4 * popcount(pe->passed_pawns(strongSide));
else if ( pos.non_pawn_material(WHITE) == RookValueMg
&& pos.non_pawn_material(BLACK) == RookValueMg
&& pos.count<PAWN>(strongSide) - pos.count<PAWN>(~strongSide) <= 1
&& bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN))
&& (attacks_bb<KING>(pos.square<KING>(~strongSide)) & pos.pieces(~strongSide, PAWN)))
sf = 36;
else if ( pos.non_pawn_material(WHITE) == RookValueMg
&& pos.non_pawn_material(BLACK) == RookValueMg
&& pos.count<PAWN>(strongSide) - pos.count<PAWN>(~strongSide) <= 1
&& bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN))
&& (attacks_bb<KING>(pos.square<KING>(~strongSide)) & pos.pieces(~strongSide, PAWN)))
sf = 36;
else if (pos.count<QUEEN>() == 1)
sf = 37 + 3 * (pos.count<QUEEN>(WHITE) == 1 ? pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK)
: pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE));
else if (pos.count<QUEEN>() == 1)
sf = 37 + 3 * (pos.count<QUEEN>(WHITE) == 1 ? pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK)
: pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE));
{
// Scale and shift NNUE for compatibility with search and classical evaluation
auto adjusted_NNUE = [&](){
{
// Scale and shift NNUE for compatibility with search and classical evaluation
auto adjusted_NNUE = [&](){
- int mat = pos.non_pawn_material() + PieceValue[MG][PAWN] * pos.count<PAWN>();
- return NNUE::evaluate(pos) * (720 + mat / 32) / 1024 + Tempo;
+ int mat = pos.non_pawn_material() + PawnValueMg * pos.count<PAWN>();
+ return NNUE::evaluate(pos) * (679 + mat / 32) / 1024 + Tempo;
bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50;
bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB));
bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50;
bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB));
- v = classical ? Evaluation<NO_TRACE>(pos).value() : adjusted_NNUE();
+ // 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<PAWN>() < 2;
+
+ v = classical || strongClassical ? Evaluation<NO_TRACE>(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 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
- && (abs(v) * 16 < NNUEThreshold2 * r50
- || ( pos.opposite_bishops()
- && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50
- && !(pos.this_thread()->nodes & 0xB))))
+ if ( largePsq && !strongClassical
+ && ( abs(v) * 16 < NNUEThreshold2 * r50
+ || ( pos.opposite_bishops()
+ && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50
+ && !(pos.this_thread()->nodes & 0xB))))