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-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
// which piece type attacks which one. Attacks on lesser pieces which are
// pawn-defended are not considered.
constexpr Score ThreatByMinor[PIECE_TYPE_NB] = {
- S(0, 0), S(6, 32), S(59, 41), S(79, 56), S(90, 119), S(79, 161)
+ S(0, 0), S(5, 32), S(57, 41), S(77, 56), S(88, 119), S(79, 161)
};
constexpr Score ThreatByRook[PIECE_TYPE_NB] = {
- S(0, 0), S(3, 44), S(38, 71), S(38, 61), S(0, 38), S(51, 38)
+ S(0, 0), S(2, 44), S(36, 71), S(36, 61), S(0, 38), S(51, 38)
};
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn
};
// Assorted bonuses and penalties
- constexpr Score BishopPawns = S( 3, 7);
- constexpr Score CorneredBishop = S( 50, 50);
- constexpr Score FlankAttacks = S( 8, 0);
- constexpr Score Hanging = S( 69, 36);
- constexpr Score KingProtector = S( 7, 8);
- constexpr Score KnightOnQueen = S( 16, 12);
- constexpr Score LongDiagonalBishop = S( 45, 0);
- constexpr Score MinorBehindPawn = S( 18, 3);
- constexpr Score Outpost = S( 32, 10);
- constexpr Score PassedFile = S( 11, 8);
- constexpr Score PawnlessFlank = S( 17, 95);
- constexpr Score RestrictedPiece = S( 7, 7);
- constexpr Score RookOnQueenFile = S( 7, 6);
- constexpr Score SliderOnQueen = S( 59, 18);
- constexpr Score ThreatByKing = S( 24, 89);
- constexpr Score ThreatByPawnPush = S( 48, 39);
- constexpr Score ThreatBySafePawn = S(173, 94);
- constexpr Score TrappedRook = S( 47, 4);
- constexpr Score WeakQueen = S( 49, 15);
+ constexpr Score BishopPawns = S( 3, 7);
+ constexpr Score CorneredBishop = S( 50, 50);
+ constexpr Score FlankAttacks = S( 8, 0);
+ constexpr Score Hanging = S( 69, 36);
+ constexpr Score KingProtector = S( 7, 8);
+ constexpr Score KnightOnQueen = S( 16, 12);
+ constexpr Score LongDiagonalBishop = S( 45, 0);
+ constexpr Score MinorBehindPawn = S( 18, 3);
+ constexpr Score Outpost = S( 30, 21);
+ constexpr Score PassedFile = S( 11, 8);
+ constexpr Score PawnlessFlank = S( 17, 95);
+ constexpr Score RestrictedPiece = S( 7, 7);
+ constexpr Score RookOnQueenFile = S( 7, 6);
+ constexpr Score SliderOnQueen = S( 59, 18);
+ constexpr Score ThreatByKing = S( 24, 89);
+ constexpr Score ThreatByPawnPush = S( 48, 39);
+ constexpr Score ThreatBySafePawn = S(173, 94);
+ constexpr Score TrappedRook = S( 52, 10);
+ constexpr Score WeakQueen = S( 49, 15);
+ constexpr Score WeakQueenProtection = S( 14, 0);
#undef S
void Evaluation<T>::initialize() {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
- constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
+ constexpr Direction Up = pawn_push(Us);
+ constexpr Direction Down = -Up;
constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB : Rank7BB | Rank6BB);
const Square ksq = pos.square<KING>(Us);
// Find our pawns that are blocked or on the first two ranks
Bitboard b = pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | LowRanks);
- // Squares occupied by those pawns, by our king or queen or controlled by
- // enemy pawns are excluded from the mobility area.
- mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them));
+ // Squares occupied by those pawns, by our king or queen, by blockers to attacks on our king
+ // or controlled by enemy pawns are excluded from the mobility area.
+ mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pos.blockers_for_king(Us) | pe->pawn_attacks(Them));
// Initialize attackedBy[] for king and pawns
attackedBy[Us][KING] = pos.attacks_from<KING>(ksq);
attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
// Init our king safety tables
- kingRing[Us] = attackedBy[Us][KING];
- if (relative_rank(Us, ksq) == RANK_1)
- kingRing[Us] |= shift<Up>(kingRing[Us]);
-
- if (file_of(ksq) == FILE_H)
- kingRing[Us] |= shift<WEST>(kingRing[Us]);
-
- else if (file_of(ksq) == FILE_A)
- kingRing[Us] |= shift<EAST>(kingRing[Us]);
+ Square s = make_square(Utility::clamp(file_of(ksq), FILE_B, FILE_G),
+ Utility::clamp(rank_of(ksq), RANK_2, RANK_7));
+ kingRing[Us] = PseudoAttacks[KING][s] | s;
kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
Score Evaluation<T>::pieces() {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
+ constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
: Rank5BB | Rank4BB | Rank3BB);
const Square* pl = pos.squares<Pt>(Us);
// Bonus for bishop on a long diagonal which can "see" both center squares
if (more_than_one(attacks_bb<BISHOP>(s, pos.pieces(PAWN)) & Center))
score += LongDiagonalBishop;
- }
- // An important Chess960 pattern: A cornered bishop blocked by a friendly
- // pawn diagonally in front of it is a very serious problem, especially
- // when that pawn is also blocked.
- if ( Pt == BISHOP
- && pos.is_chess960()
- && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
- {
- 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;
+ // An important Chess960 pattern: a cornered bishop blocked by a friendly
+ // pawn diagonally in front of it is a very serious problem, especially
+ // when that pawn is also blocked.
+ if ( pos.is_chess960()
+ && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
+ {
+ 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;
+ }
}
}
constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB
: AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB);
- Bitboard weak, b1, b2, safe, unsafeChecks = 0;
+ Bitboard weak, b1, b2, b3, safe, unsafeChecks = 0;
Bitboard rookChecks, queenChecks, bishopChecks, knightChecks;
int kingDanger = 0;
const Square ksq = pos.square<KING>(Us);
else
unsafeChecks |= knightChecks;
- // Find the squares that opponent attacks in our king flank, and the squares
- // which are attacked twice in that flank.
+ // Find the squares that opponent attacks in our king flank, the squares
+ // which they attack twice in that flank, and the squares that we defend.
b1 = attackedBy[Them][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
b2 = b1 & attackedBy2[Them];
+ b3 = attackedBy[Us][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
- int kingFlankAttacks = popcount(b1) + popcount(b2);
+ int kingFlankAttack = popcount(b1) + popcount(b2);
+ int kingFlankDefense = popcount(b3);
kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
+ 185 * popcount(kingRing[Us] & weak)
+ 148 * popcount(unsafeChecks)
+ 98 * popcount(pos.blockers_for_king(Us))
+ 69 * kingAttacksCount[Them]
- + 3 * kingFlankAttacks * kingFlankAttacks / 8
+ + 3 * kingFlankAttack * kingFlankAttack / 8
+ mg_value(mobility[Them] - mobility[Us])
- 873 * !pos.count<QUEEN>(Them)
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
- - 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING])
- 6 * mg_value(score) / 8
- - 7;
+ - 4 * kingFlankDefense
+ + 37;
// Transform the kingDanger units into a Score, and subtract it from the evaluation
if (kingDanger > 100)
score -= PawnlessFlank;
// Penalty if king flank is under attack, potentially moving toward the king
- score -= FlankAttacks * kingFlankAttacks;
+ score -= FlankAttacks * kingFlankAttack;
if (T)
Trace::add(KING, Us, score);
Score Evaluation<T>::threats() const {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
+ constexpr Direction Up = pawn_push(Us);
constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe;
b = ~attackedBy[Them][ALL_PIECES]
| (nonPawnEnemies & attackedBy2[Us]);
score += Hanging * popcount(weak & b);
+
+ // Additional bonus if weak piece is only protected by a queen
+ score += WeakQueenProtection * popcount(weak & attackedBy[Them][QUEEN]);
}
// Bonus for restricting their piece moves
b = attackedBy[Them][ALL_PIECES]
& ~stronglyProtected
& attackedBy[Us][ALL_PIECES];
-
score += RestrictedPiece * popcount(b);
// Protected or unattacked squares
Score Evaluation<T>::passed() const {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
+ constexpr Direction Up = pawn_push(Us);
auto king_proximity = [&](Color c, Square s) {
return std::min(distance(pos.square<KING>(c), s), 5);
Square blockSq = s + Up;
// Adjust bonus based on the king's proximity
- bonus += make_score(0, ( king_proximity(Them, blockSq) * 5
- - king_proximity(Us, blockSq) * 2) * w);
+ bonus += make_score(0, ( (king_proximity(Them, blockSq) * 19) / 4
+ - king_proximity(Us, blockSq) * 2) * w);
// If blockSq is not the queening square then consider also a second push
if (r != RANK_7)
return SCORE_ZERO;
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
+ constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard SpaceMask =
Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB)
: CenterFiles & (Rank7BB | Rank6BB | Rank5BB);
template<Tracing T>
Score Evaluation<T>::initiative(Score score) const {
- Value mg = mg_value(score);
- Value eg = eg_value(score);
-
int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
- distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
&& outflanking < 0
&& !pawnsOnBothFlanks;
+ bool infiltration = rank_of(pos.square<KING>(WHITE)) > RANK_4
+ || rank_of(pos.square<KING>(BLACK)) < RANK_5;
+
// Compute the initiative bonus for the attacking side
int complexity = 9 * pe->passed_count()
+ 11 * pos.count<PAWN>()
+ 9 * outflanking
- + 18 * pawnsOnBothFlanks
- + 49 * !pos.non_pawn_material()
- - 36 * almostUnwinnable
- -103 ;
+ + 21 * pawnsOnBothFlanks
+ + 24 * infiltration
+ + 51 * !pos.non_pawn_material()
+ - 43 * almostUnwinnable
+ -110 ;
+
+ Value mg = mg_value(score);
+ Value eg = eg_value(score);
// Now apply the bonus: note that we find the attacking side by extracting the
// sign of the midgame or endgame values, and that we carefully cap the bonus
{
if ( pos.opposite_bishops()
&& pos.non_pawn_material() == 2 * BishopValueMg)
- sf = 16 + 4 * pe->passed_count();
+ sf = 22 ;
else
sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide));
Trace::add(TOTAL, score);
}
- return (pos.side_to_move() == WHITE ? v : -v) // Side to move point of view
- + Eval::Tempo;
+ return (pos.side_to_move() == WHITE ? v : -v) + Tempo; // Side to move point of view
}
} // namespace
std::string Eval::trace(const Position& pos) {
+ if (pos.checkers())
+ return "Total evaluation: none (in check)";
+
std::memset(scores, 0, sizeof(scores));
pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt