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-2018 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
namespace {
- constexpr Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB;
- constexpr Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
- constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
- constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB);
-
- constexpr Bitboard KingFlank[FILE_NB] = {
- QueenSide, QueenSide, QueenSide,
- CenterFiles, CenterFiles,
- KingSide, KingSide, KingSide
- };
-
// Threshold for lazy and space evaluation
- constexpr Value LazyThreshold = Value(1500);
+ constexpr Value LazyThreshold = Value(1400);
constexpr Value SpaceThreshold = Value(12222);
// KingAttackWeights[PieceType] contains king attack weights by piece type
- constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 78, 56, 45, 11 };
+ constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 };
// Penalties for enemy's safe checks
constexpr int QueenSafeCheck = 780;
- constexpr int RookSafeCheck = 880;
- constexpr int BishopSafeCheck = 435;
+ constexpr int RookSafeCheck = 1080;
+ constexpr int BishopSafeCheck = 635;
constexpr int KnightSafeCheck = 790;
#define S(mg, eg) make_score(mg, eg)
// MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game,
// indexed by piece type and number of attacked squares in the mobility area.
constexpr Score MobilityBonus[][32] = {
- { S(-75,-76), S(-57,-54), S( -9,-28), S( -2,-10), S( 6, 5), S( 14, 12), // Knights
- S( 22, 26), S( 29, 29), S( 36, 29) },
- { S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishops
+ { S(-62,-81), S(-53,-56), S(-12,-30), S( -4,-14), S( 3, 8), S( 13, 15), // Knight
+ S( 22, 23), S( 28, 27), S( 33, 33) },
+ { S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishop
S( 55, 54), S( 63, 57), S( 63, 65), S( 68, 73), S( 81, 78), S( 81, 86),
S( 91, 88), S( 98, 97) },
- { S(-58,-76), S(-27,-18), S(-15, 28), S(-10, 55), S( -5, 69), S( -2, 82), // Rooks
+ { S(-58,-76), S(-27,-18), S(-15, 28), S(-10, 55), S( -5, 69), S( -2, 82), // Rook
S( 9,112), S( 16,118), S( 30,132), S( 29,142), S( 32,155), S( 38,165),
S( 46,166), S( 48,169), S( 58,171) },
- { S(-39,-36), S(-21,-15), S( 3, 8), S( 3, 18), S( 14, 34), S( 22, 54), // Queens
+ { S(-39,-36), S(-21,-15), S( 3, 8), S( 3, 18), S( 14, 34), S( 22, 54), // Queen
S( 28, 61), S( 41, 73), S( 43, 79), S( 48, 92), S( 56, 94), S( 60,104),
S( 60,113), S( 66,120), S( 67,123), S( 70,126), S( 71,133), S( 73,136),
S( 79,140), S( 88,143), S( 88,148), S( 99,166), S(102,170), S(102,175),
S(106,184), S(109,191), S(113,206), S(116,212) }
};
- // Outpost[knight/bishop][supported by pawn] contains bonuses for minor
- // pieces if they occupy or can reach an outpost square, bigger if that
- // square is supported by a pawn.
- constexpr Score Outpost[][2] = {
- { S(22, 6), S(36,12) }, // Knight
- { S( 9, 2), S(15, 5) } // Bishop
- };
-
// RookOnFile[semiopen/open] contains bonuses for each rook when there is
// no (friendly) pawn on the rook file.
- constexpr Score RookOnFile[] = { S(20, 7), S(45, 20) };
+ constexpr Score RookOnFile[] = { S(21, 4), S(47, 25) };
// ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to
// 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(0, 31), S(39, 42), S(57, 44), S(68, 112), S(47, 120)
+ 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(0, 24), S(38, 71), S(38, 61), S(0, 38), S(36, 38)
+ S(0, 0), S(2, 44), S(36, 71), S(36, 61), S(0, 38), S(51, 38)
};
- // ThreatByKing[on one/on many] contains bonuses for king attacks on
- // pawns or pieces which are not pawn-defended.
- constexpr Score ThreatByKing[] = { S(3, 65), S(9, 145) };
-
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn
constexpr Score PassedRank[RANK_NB] = {
- S(0, 0), S(5, 7), S(5, 13), S(32, 42), S(70, 70), S(172, 170), S(217, 269)
- };
-
- // PassedFile[File] contains a bonus according to the file of a passed pawn
- constexpr Score PassedFile[FILE_NB] = {
- S( 9, 10), S(2, 10), S(1, -8), S(-20,-12),
- S(-20,-12), S(1, -8), S(2, 10), S( 9, 10)
+ S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
};
- // PassedDanger[Rank] contains a term to weight the passed score
- constexpr int PassedDanger[RANK_NB] = { 0, 0, 0, 2, 7, 12, 19 };
-
- // KingProtector[PieceType-2] contains a penalty according to distance from king
- constexpr Score KingProtector[] = { S(3, 5), S(4, 3), S(3, 0), S(1, -1) };
-
// Assorted bonuses and penalties
- constexpr Score BishopPawns = S( 8, 12);
- constexpr Score CloseEnemies = S( 7, 0);
- constexpr Score Connectivity = S( 3, 1);
- constexpr Score CorneredBishop = S( 50, 50);
- constexpr Score Hanging = S( 52, 30);
- constexpr Score HinderPassedPawn = S( 8, 1);
- constexpr Score KnightOnQueen = S( 21, 11);
- constexpr Score LongDiagonalBishop = S( 22, 0);
- constexpr Score MinorBehindPawn = S( 16, 0);
- constexpr Score PawnlessFlank = S( 20, 80);
- constexpr Score RookOnPawn = S( 8, 24);
- constexpr Score SliderOnQueen = S( 42, 21);
- constexpr Score ThreatByPawnPush = S( 47, 26);
- constexpr Score ThreatByRank = S( 16, 3);
- constexpr Score ThreatBySafePawn = S(175,168);
- constexpr Score TrappedRook = S( 92, 0);
- constexpr Score WeakQueen = S( 50, 10);
- constexpr Score WeakUnopposedPawn = S( 5, 25);
+ 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
template<Color Us> Score passed() const;
template<Color Us> Score space() const;
ScaleFactor scale_factor(Value eg) const;
- Score initiative(Value eg) const;
+ Score initiative(Score score) const;
const Position& pos;
Material::Entry* me;
// is also calculated is ALL_PIECES.
Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
- // attackedBy2[color] are the squares attacked by 2 pieces of a given color,
- // possibly via x-ray or by one pawn and one piece. Diagonal x-ray through
- // pawn or squares attacked by 2 pawns are not explicitly added.
+ // attackedBy2[color] are the squares attacked by at least 2 units of a given
+ // color, including x-rays. But diagonal x-rays through pawns are not computed.
Bitboard attackedBy2[COLOR_NB];
- // kingRing[color] are the squares adjacent to the king, plus (only for a
- // king on its first rank) the squares two ranks in front. For instance,
- // if black's king is on g8, kingRing[BLACK] is f8, h8, f7, g7, h7, f6, g6
- // and h6. It is set to 0 when king safety evaluation is skipped.
+ // kingRing[color] are the squares adjacent to the king plus some other
+ // very near squares, depending on king position.
Bitboard kingRing[COLOR_NB];
// kingAttackersCount[color] is the number of pieces of the given color
template<Tracing T> template<Color Us>
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 Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB: Rank7BB | Rank6BB);
+ constexpr Color Them = ~Us;
+ 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);
+
+ Bitboard dblAttackByPawn = pawn_double_attacks_bb<Us>(pos.pieces(Us, PAWN));
// 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 controlled by enemy pawns
- // are excluded from the mobility area.
- mobilityArea[Us] = ~(b | pos.square<KING>(Us) | 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));
- // Initialise attackedBy bitboards for kings and pawns
- attackedBy[Us][KING] = pos.attacks_from<KING>(pos.square<KING>(Us));
+ // Initialize attackedBy[] for king and pawns
+ attackedBy[Us][KING] = pos.attacks_from<KING>(ksq);
attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
- attackedBy2[Us] = attackedBy[Us][KING] & attackedBy[Us][PAWN];
+ attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
- // Init our king safety tables only if we are going to use them
- if (pos.non_pawn_material(Them) >= RookValueMg + KnightValueMg)
- {
- kingRing[Us] = attackedBy[Us][KING];
- if (relative_rank(Us, pos.square<KING>(Us)) == RANK_1)
- kingRing[Us] |= shift<Up>(kingRing[Us]);
+ // Init our king safety tables
+ 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;
- if (file_of(pos.square<KING>(Us)) == FILE_H)
- kingRing[Us] |= shift<WEST>(kingRing[Us]);
+ kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
+ kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
- else if (file_of(pos.square<KING>(Us)) == FILE_A)
- kingRing[Us] |= shift<EAST>(kingRing[Us]);
-
- kingAttackersCount[Them] = popcount(attackedBy[Us][KING] & pe->pawn_attacks(Them));
- kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
- }
- else
- kingRing[Us] = kingAttackersCount[Them] = 0;
+ // Remove from kingRing[] the squares defended by two pawns
+ kingRing[Us] &= ~dblAttackByPawn;
}
template<Tracing T> template<Color Us, PieceType Pt>
Score Evaluation<T>::pieces() {
- constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
+ constexpr Color Them = ~Us;
+ constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
: Rank5BB | Rank4BB | Rank3BB);
const Square* pl = pos.squares<Pt>(Us);
Bitboard b, bb;
- Square s;
Score score = SCORE_ZERO;
attackedBy[Us][Pt] = 0;
- while ((s = *pl++) != SQ_NONE)
+ for (Square s = *pl; s != SQ_NONE; s = *++pl)
{
// Find attacked squares, including x-ray attacks for bishops and rooks
b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN))
mobility[Us] += MobilityBonus[Pt - 2][mob];
- // Penalty if the piece is far from the king
- score -= KingProtector[Pt - 2] * distance(s, pos.square<KING>(Us));
-
if (Pt == BISHOP || Pt == KNIGHT)
{
// Bonus if piece is on an outpost square or can reach one
- bb = OutpostRanks & ~pe->pawn_attacks_span(Them);
+ bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them);
if (bb & s)
- score += Outpost[Pt == BISHOP][bool(attackedBy[Us][PAWN] & s)] * 2;
+ score += Outpost * (Pt == KNIGHT ? 2 : 1);
- else if (bb &= b & ~pos.pieces(Us))
- score += Outpost[Pt == BISHOP][bool(attackedBy[Us][PAWN] & bb)];
+ else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
+ score += Outpost;
- // Bonus when behind a pawn
- if ( relative_rank(Us, s) < RANK_5
- && (pos.pieces(PAWN) & (s + pawn_push(Us))))
+ // Bonus for a knight or bishop shielded by pawn
+ if (shift<Down>(pos.pieces(PAWN)) & s)
score += MinorBehindPawn;
+ // Penalty if the piece is far from the king
+ score -= KingProtector * distance(pos.square<KING>(Us), s);
+
if (Pt == BISHOP)
{
- // Penalty according to number of pawns on the same color square as the bishop
- score -= BishopPawns * pe->pawns_on_same_color_squares(Us, s);
+ // Penalty according to number of pawns on the same color square as the
+ // bishop, bigger when the center files are blocked with pawns and smaller
+ // when the bishop is outside the pawn chain.
+ Bitboard blocked = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces());
+
+ score -= BishopPawns * pos.pawns_on_same_color_squares(Us, s)
+ * (!(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles));
// Bonus for bishop on a long diagonal which can "see" both center squares
- if (more_than_one(Center & (attacks_bb<BISHOP>(s, pos.pieces(PAWN)) | s)))
+ 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;
+ }
}
}
if (Pt == ROOK)
{
- // Bonus for aligning rook with enemy pawns on the same rank/file
- if (relative_rank(Us, s) >= RANK_5)
- score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]);
+ // Bonus for rook on the same file as a queen
+ if (file_bb(s) & pos.pieces(QUEEN))
+ score += RookOnQueenFile;
// Bonus for rook on an open or semi-open file
- if (pe->semiopen_file(Us, file_of(s)))
- score += RookOnFile[bool(pe->semiopen_file(Them, file_of(s)))];
+ if (pos.is_on_semiopen_file(Us, s))
+ score += RookOnFile[pos.is_on_semiopen_file(Them, s)];
// Penalty when trapped by the king, even more if the king cannot castle
else if (mob <= 3)
{
File kf = file_of(pos.square<KING>(Us));
if ((kf < FILE_E) == (file_of(s) < kf))
- score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us));
+ score -= TrappedRook * (1 + !pos.castling_rights(Us));
}
}
template<Tracing T> template<Color Us>
Score Evaluation<T>::king() const {
- constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
+ constexpr Color Them = ~Us;
constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB
: AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB);
+ Bitboard weak, b1, b2, b3, safe, unsafeChecks = 0;
+ Bitboard rookChecks, queenChecks, bishopChecks, knightChecks;
+ int kingDanger = 0;
const Square ksq = pos.square<KING>(Us);
- Bitboard weak, b, b1, b2, safe, unsafeChecks, pinned;
- // King shelter and enemy pawns storm
- Score score = pe->king_safety<Us>(pos, ksq);
+ // Init the score with king shelter and enemy pawns storm
+ Score score = pe->king_safety<Us>(pos);
- // Main king safety evaluation
- if (kingAttackersCount[Them] > 1 - pos.count<QUEEN>(Them))
- {
- int kingDanger = 0;
- unsafeChecks = 0;
+ // Attacked squares defended at most once by our queen or king
+ weak = attackedBy[Them][ALL_PIECES]
+ & ~attackedBy2[Us]
+ & (~attackedBy[Us][ALL_PIECES] | attackedBy[Us][KING] | attackedBy[Us][QUEEN]);
- // Attacked squares defended at most once by our queen or king
- weak = attackedBy[Them][ALL_PIECES]
- & ~attackedBy2[Us]
- & (~attackedBy[Us][ALL_PIECES] | attackedBy[Us][KING] | attackedBy[Us][QUEEN]);
+ // Analyse the safe enemy's checks which are possible on next move
+ safe = ~pos.pieces(Them);
+ safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]);
- // Analyse the safe enemy's checks which are possible on next move
- safe = ~pos.pieces(Them);
- safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]);
+ b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
+ b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
- b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
- b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
-
- // Enemy queen safe checks
- if ((b1 | b2) & attackedBy[Them][QUEEN] & safe & ~attackedBy[Us][QUEEN])
- kingDanger += QueenSafeCheck;
-
- b1 &= attackedBy[Them][ROOK];
- b2 &= attackedBy[Them][BISHOP];
-
- // Enemy rooks checks
- if (b1 & safe)
- kingDanger += RookSafeCheck;
- else
- unsafeChecks |= b1;
+ // Enemy rooks checks
+ rookChecks = b1 & safe & attackedBy[Them][ROOK];
- // Enemy bishops checks
- if (b2 & safe)
- kingDanger += BishopSafeCheck;
- else
- unsafeChecks |= b2;
+ if (rookChecks)
+ kingDanger += RookSafeCheck;
+ else
+ unsafeChecks |= b1 & attackedBy[Them][ROOK];
+
+ // Enemy queen safe checks: we count them only if they are from squares from
+ // which we can't give a rook check, because rook checks are more valuable.
+ queenChecks = (b1 | b2)
+ & attackedBy[Them][QUEEN]
+ & safe
+ & ~attackedBy[Us][QUEEN]
+ & ~rookChecks;
+
+ if (queenChecks)
+ kingDanger += QueenSafeCheck;
+
+ // Enemy bishops checks: we count them only if they are from squares from
+ // which we can't give a queen check, because queen checks are more valuable.
+ bishopChecks = b2
+ & attackedBy[Them][BISHOP]
+ & safe
+ & ~queenChecks;
+
+ if (bishopChecks)
+ kingDanger += BishopSafeCheck;
+ else
+ unsafeChecks |= b2 & attackedBy[Them][BISHOP];
- // Enemy knights checks
- b = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
- if (b & safe)
- kingDanger += KnightSafeCheck;
- else
- unsafeChecks |= b;
-
- // Unsafe or occupied checking squares will also be considered, as long as
- // the square is in the attacker's mobility area.
- unsafeChecks &= mobilityArea[Them];
- pinned = pos.blockers_for_king(Us) & pos.pieces(Us);
-
- kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
- + 102 * kingAttacksCount[Them]
- + 191 * popcount(kingRing[Us] & weak)
- + 143 * popcount(pinned | unsafeChecks)
- - 848 * !pos.count<QUEEN>(Them)
- - 9 * mg_value(score) / 8
- + 40;
-
- // Transform the kingDanger units into a Score, and subtract it from the evaluation
- if (kingDanger > 0)
- {
- int mobilityDanger = mg_value(mobility[Them] - mobility[Us]);
- kingDanger = std::max(0, kingDanger + mobilityDanger);
- score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);
- }
- }
+ // Enemy knights checks
+ knightChecks = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
- Bitboard kf = KingFlank[file_of(ksq)];
+ if (knightChecks & safe)
+ kingDanger += KnightSafeCheck;
+ else
+ unsafeChecks |= knightChecks;
+
+ // 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 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 * 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;
+
+ // Transform the kingDanger units into a Score, and subtract it from the evaluation
+ if (kingDanger > 100)
+ score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);
// Penalty when our king is on a pawnless flank
- if (!(pos.pieces(PAWN) & kf))
+ if (!(pos.pieces(PAWN) & KingFlank[file_of(ksq)]))
score -= PawnlessFlank;
- // Find the squares that opponent attacks in our king flank, and the squares
- // which are attacked twice in that flank but not defended by our pawns.
- b1 = attackedBy[Them][ALL_PIECES] & kf & Camp;
- b2 = b1 & attackedBy2[Them] & ~attackedBy[Us][PAWN];
-
- // King tropism, to anticipate slow motion attacks on our king
- score -= CloseEnemies * (popcount(b1) + popcount(b2));
+ // Penalty if king flank is under attack, potentially moving toward the king
+ score -= FlankAttacks * kingFlankAttack;
if (T)
Trace::add(KING, Us, score);
template<Tracing T> template<Color Us>
Score Evaluation<T>::threats() const {
- constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
+ constexpr Color Them = ~Us;
+ constexpr Direction Up = pawn_push(Us);
constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
- Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safeThreats;
+ Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe;
Score score = SCORE_ZERO;
- // Non-pawn enemies attacked by a pawn
- nonPawnEnemies = pos.pieces(Them) ^ pos.pieces(Them, PAWN);
- weak = nonPawnEnemies & attackedBy[Us][PAWN];
-
- if (weak)
- {
- // Our safe or protected pawns
- b = pos.pieces(Us, PAWN)
- & (~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES]);
-
- safeThreats = pawn_attacks_bb<Us>(b) & weak;
- score += ThreatBySafePawn * popcount(safeThreats);
- }
+ // Non-pawn enemies
+ nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(PAWN);
// Squares strongly protected by the enemy, either because they defend the
// square with a pawn, or because they defend the square twice and we don't.
{
b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]);
while (b)
- {
- Square s = pop_lsb(&b);
- score += ThreatByMinor[type_of(pos.piece_on(s))];
- if (type_of(pos.piece_on(s)) != PAWN)
- score += ThreatByRank * (int)relative_rank(Them, s);
- }
+ score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(&b)))];
- b = (pos.pieces(Them, QUEEN) | weak) & attackedBy[Us][ROOK];
+ b = weak & attackedBy[Us][ROOK];
while (b)
- {
- Square s = pop_lsb(&b);
- score += ThreatByRook[type_of(pos.piece_on(s))];
- if (type_of(pos.piece_on(s)) != PAWN)
- score += ThreatByRank * (int)relative_rank(Them, s);
- }
+ score += ThreatByRook[type_of(pos.piece_on(pop_lsb(&b)))];
- score += Hanging * popcount(weak & ~attackedBy[Them][ALL_PIECES]);
+ if (weak & attackedBy[Us][KING])
+ score += ThreatByKing;
- b = weak & attackedBy[Us][KING];
- if (b)
- score += ThreatByKing[more_than_one(b)];
+ 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 enemy unopposed weak pawns
- if (pos.pieces(Us, ROOK, QUEEN))
- score += WeakUnopposedPawn * pe->weak_unopposed(Them);
+ // Bonus for restricting their piece moves
+ b = attackedBy[Them][ALL_PIECES]
+ & ~stronglyProtected
+ & attackedBy[Us][ALL_PIECES];
+ score += RestrictedPiece * popcount(b);
+
+ // Protected or unattacked squares
+ safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES];
+
+ // Bonus for attacking enemy pieces with our relatively safe pawns
+ b = pos.pieces(Us, PAWN) & safe;
+ b = pawn_attacks_bb<Us>(b) & nonPawnEnemies;
+ score += ThreatBySafePawn * popcount(b);
// Find squares where our pawns can push on the next move
b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces();
b |= shift<Up>(b & TRank3BB) & ~pos.pieces();
- // Keep only the squares which are not completely unsafe
- b &= ~attackedBy[Them][PAWN]
- & (attackedBy[Us][ALL_PIECES] | ~attackedBy[Them][ALL_PIECES]);
+ // Keep only the squares which are relatively safe
+ b &= ~attackedBy[Them][PAWN] & safe;
// Bonus for safe pawn threats on the next move
- b = pawn_attacks_bb<Us>(b)
- & pos.pieces(Them)
- & ~attackedBy[Us][PAWN];
-
+ b = pawn_attacks_bb<Us>(b) & nonPawnEnemies;
score += ThreatByPawnPush * popcount(b);
// Bonus for threats on the next moves against enemy queen
if (pos.count<QUEEN>(Them) == 1)
{
Square s = pos.square<QUEEN>(Them);
- safeThreats = mobilityArea[Us] & ~stronglyProtected;
+ safe = mobilityArea[Us] & ~stronglyProtected;
b = attackedBy[Us][KNIGHT] & pos.attacks_from<KNIGHT>(s);
- score += KnightOnQueen * popcount(b & safeThreats);
+ score += KnightOnQueen * popcount(b & safe);
b = (attackedBy[Us][BISHOP] & pos.attacks_from<BISHOP>(s))
| (attackedBy[Us][ROOK ] & pos.attacks_from<ROOK >(s));
- score += SliderOnQueen * popcount(b & safeThreats & attackedBy2[Us]);
+ score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]);
}
- // Connectivity: ensure that knights, bishops, rooks, and queens are protected
- b = (pos.pieces(Us) ^ pos.pieces(Us, PAWN, KING)) & attackedBy[Us][ALL_PIECES];
- score += Connectivity * popcount(b);
-
if (T)
Trace::add(THREAT, Us, score);
template<Tracing T> template<Color Us>
Score Evaluation<T>::passed() const {
- constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
+ constexpr Color Them = ~Us;
+ constexpr Direction Up = pawn_push(Us);
auto king_proximity = [&](Color c, Square s) {
return std::min(distance(pos.square<KING>(c), s), 5);
};
- Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares;
+ Bitboard b, bb, squaresToQueen, unsafeSquares;
Score score = SCORE_ZERO;
b = pe->passed_pawns(Us);
assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up)));
- bb = forward_file_bb(Us, s) & (attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
- score -= HinderPassedPawn * popcount(bb);
-
int r = relative_rank(Us, s);
- int w = PassedDanger[r];
Score bonus = PassedRank[r];
- if (w)
+ if (r > RANK_3)
{
+ int w = 5 * r - 13;
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)
// If the pawn is free to advance, then increase the bonus
if (pos.empty(blockSq))
{
- // If there is a rook or queen attacking/defending the pawn from behind,
- // consider all the squaresToQueen. Otherwise consider only the squares
- // in the pawn's path attacked or occupied by the enemy.
- defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s);
+ squaresToQueen = forward_file_bb(Us, s);
+ unsafeSquares = passed_pawn_span(Us, s);
- bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(s);
-
- if (!(pos.pieces(Us) & bb))
- defendedSquares &= attackedBy[Us][ALL_PIECES];
+ bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN);
if (!(pos.pieces(Them) & bb))
- unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
-
- // If there aren't any enemy attacks, assign a big bonus. Otherwise
- // assign a smaller bonus if the block square isn't attacked.
- int k = !unsafeSquares ? 20 : !(unsafeSquares & blockSq) ? 9 : 0;
+ unsafeSquares &= attackedBy[Them][ALL_PIECES];
- // If the path to the queen is fully defended, assign a big bonus.
- // Otherwise assign a smaller bonus if the block square is defended.
- if (defendedSquares == squaresToQueen)
- k += 6;
+ // If there are no enemy attacks on passed pawn span, assign a big 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.
+ int k = !unsafeSquares ? 35 :
+ !(unsafeSquares & squaresToQueen) ? 20 :
+ !(unsafeSquares & blockSq) ? 9 :
+ 0 ;
- else if (defendedSquares & blockSq)
- k += 4;
+ // Assign a larger bonus if the block square is defended
+ if ((pos.pieces(Us) & bb) || (attackedBy[Us][ALL_PIECES] & blockSq))
+ k += 5;
bonus += make_score(k * w, k * w);
}
- else if (pos.pieces(Us) & blockSq)
- bonus += make_score(w + r * 2, w + r * 2);
- } // w != 0
+ } // r > RANK_3
- // Scale down bonus for candidate passers which need more than one
- // pawn push to become passed or have a pawn in front of them.
- if ( !pos.pawn_passed(Us, s + Up)
- || (pos.pieces(PAWN) & forward_file_bb(Us, s)))
- bonus = bonus / 2;
-
- score += bonus + PassedFile[file_of(s)];
+ score += bonus - PassedFile * edge_distance(file_of(s));
}
if (T)
template<Tracing T> template<Color Us>
Score Evaluation<T>::space() const {
- constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
+ if (pos.non_pawn_material() < SpaceThreshold)
+ return SCORE_ZERO;
+
+ constexpr Color Them = ~Us;
+ constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard SpaceMask =
Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB)
: CenterFiles & (Rank7BB | Rank6BB | Rank5BB);
- if (pos.non_pawn_material() < SpaceThreshold)
- return SCORE_ZERO;
-
- // Find the safe squares for our pieces inside the area defined by
- // SpaceMask. A square is unsafe if it is attacked by an enemy
- // pawn, or if it is undefended and attacked by an enemy piece.
+ // Find the available squares for our pieces inside the area defined by SpaceMask
Bitboard safe = SpaceMask
& ~pos.pieces(Us, PAWN)
- & ~attackedBy[Them][PAWN]
- & (attackedBy[Us][ALL_PIECES] | ~attackedBy[Them][ALL_PIECES]);
+ & ~attackedBy[Them][PAWN];
// Find all squares which are at most three squares behind some friendly pawn
Bitboard behind = pos.pieces(Us, PAWN);
- behind |= (Us == WHITE ? behind >> 8 : behind << 8);
- behind |= (Us == WHITE ? behind >> 16 : behind << 16);
-
- int bonus = popcount(safe) + popcount(behind & safe);
- int weight = pos.count<ALL_PIECES>(Us) - 2 * pe->open_files();
+ behind |= shift<Down>(behind);
+ behind |= shift<Down+Down>(behind);
+ int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]);
+ int weight = pos.count<ALL_PIECES>(Us) - 1;
Score score = make_score(bonus * weight * weight / 16, 0);
if (T)
// known attacking/defending status of the players.
template<Tracing T>
- Score Evaluation<T>::initiative(Value eg) const {
+ Score Evaluation<T>::initiative(Score score) const {
int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
- distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide)
&& (pos.pieces(PAWN) & KingSide);
+ bool almostUnwinnable = 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 = 8 * outflanking
- + 8 * pe->pawn_asymmetry()
- + 12 * pos.count<PAWN>()
- + 16 * pawnsOnBothFlanks
- + 48 * !pos.non_pawn_material()
- -136 ;
-
- // Now apply the bonus: note that we find the attacking side by extracting
- // the sign of the endgame value, and that we carefully cap the bonus so
- // that the endgame score will never change sign after the bonus.
+ int complexity = 9 * pe->passed_count()
+ + 11 * pos.count<PAWN>()
+ + 9 * outflanking
+ + 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
+ // so that the midgame and endgame scores do not change sign after the bonus.
+ int u = ((mg > 0) - (mg < 0)) * std::max(std::min(complexity + 50, 0), -abs(mg));
int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg));
if (T)
- Trace::add(INITIATIVE, make_score(0, v));
+ Trace::add(INITIATIVE, make_score(u, v));
- return make_score(0, v);
+ return make_score(u, v);
}
Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK;
int sf = me->scale_factor(pos, strongSide);
- // If we don't already have an unusual scale factor, check for certain
- // types of endgames, and use a lower scale for those.
- if (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN)
+ // If scale is not already specific, scale down the endgame via general heuristics
+ if (sf == SCALE_FACTOR_NORMAL)
{
- if (pos.opposite_bishops())
- {
- // Endgame with opposite-colored bishops and no other pieces is almost a draw
- if ( pos.non_pawn_material(WHITE) == BishopValueMg
- && pos.non_pawn_material(BLACK) == BishopValueMg)
- sf = 31;
-
- // Endgame with opposite-colored bishops, but also other pieces. Still
- // a bit drawish, but not as drawish as with only the two bishops.
- else
- sf = 46;
- }
- // Endings where weaker side can place his king in front of the enemy's
- // pawns are drawish.
- else if ( abs(eg) <= BishopValueEg
- && pos.count<PAWN>(strongSide) <= 2
- && !pos.pawn_passed(~strongSide, pos.square<KING>(~strongSide)))
- sf = 37 + 7 * pos.count<PAWN>(strongSide);
+ if ( pos.opposite_bishops()
+ && pos.non_pawn_material() == 2 * BishopValueMg)
+ sf = 22;
+ else
+ sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide));
+
+ sf = std::max(0, sf - (pos.rule50_count() - 12) / 4);
}
return ScaleFactor(sf);
// Early exit if score is high
Value v = (mg_value(score) + eg_value(score)) / 2;
- if (abs(v) > LazyThreshold)
+ if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64)
return pos.side_to_move() == WHITE ? v : -v;
// Main evaluation begins here
+ passed< WHITE>() - passed< BLACK>()
+ space< WHITE>() - space< BLACK>();
- score += initiative(eg_value(score));
+ score += initiative(score);
// Interpolate between a middlegame and a (scaled by 'sf') endgame score
ScaleFactor sf = scale_factor(eg_value(score));
v = mg_value(score) * int(me->game_phase())
+ eg_value(score) * int(PHASE_MIDGAME - me->game_phase()) * sf / SCALE_FACTOR_NORMAL;
- v /= int(PHASE_MIDGAME);
+ v /= PHASE_MIDGAME;
// In case of tracing add all remaining individual evaluation terms
if (T)
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
<< " ------------+-------------+-------------+------------\n"
<< " Material | " << Term(MATERIAL)
<< " Imbalance | " << Term(IMBALANCE)
- << " Initiative | " << Term(INITIATIVE)
<< " Pawns | " << Term(PAWN)
<< " Knights | " << Term(KNIGHT)
<< " Bishops | " << Term(BISHOP)
<< " Threats | " << Term(THREAT)
<< " Passed | " << Term(PASSED)
<< " Space | " << Term(SPACE)
+ << " Initiative | " << Term(INITIATIVE)
<< " ------------+-------------+-------------+------------\n"
<< " Total | " << Term(TOTAL);