namespace {
+ const Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB);
+ const Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB;
+ const Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
+ const Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
+
+ const Bitboard KingFlank[FILE_NB] = {
+ QueenSide, QueenSide, QueenSide, CenterFiles, CenterFiles, KingSide, KingSide, KingSide
+ };
+
namespace Trace {
enum Tracing {NO_TRACE, TRACE};
enum Term { // The first 8 entries are for PieceType
- MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERM_NB
+ MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, INITIATIVE, TOTAL, TERM_NB
};
double scores[TERM_NB][COLOR_NB][PHASE_NB];
std::ostream& operator<<(std::ostream& os, Term t) {
- if (t == MATERIAL || t == IMBALANCE || t == Term(PAWN) || t == TOTAL)
+ if (t == MATERIAL || t == IMBALANCE || t == Term(PAWN) || t == INITIATIVE || t == TOTAL)
os << " --- --- | --- --- | ";
else
os << std::setw(5) << scores[t][WHITE][MG] << " "
public:
Evaluation() = delete;
- Evaluation(const Position& p) : pos(p) {};
+ Evaluation(const Position& p) : pos(p) {}
Evaluation& operator=(const Evaluation&) = delete;
Value value();
template<Color Us> void initialize();
template<Color Us> Score evaluate_king();
template<Color Us> Score evaluate_threats();
- template<Color Us> Score evaluate_passer_pawns();
+ template<Color Us> Score evaluate_passed_pawns();
template<Color Us> Score evaluate_space();
template<Color Us, PieceType Pt> Score evaluate_pieces();
ScaleFactor evaluate_scale_factor(Value eg);
// supported by a pawn. If the minor piece occupies an outpost square
// then score is doubled.
const Score Outpost[][2] = {
- { S(22, 6), S(33, 9) }, // Knight
- { S( 9, 2), S(14, 4) } // Bishop
+ { 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
// Assorted bonuses and penalties used by evaluation
const Score MinorBehindPawn = S( 16, 0);
const Score BishopPawns = S( 8, 12);
+ const Score LongRangedBishop = S( 22, 0);
const Score RookOnPawn = S( 8, 24);
const Score TrappedRook = S( 92, 0);
const Score WeakQueen = S( 50, 10);
const Score CloseEnemies = S( 7, 0);
const Score PawnlessFlank = S( 20, 80);
const Score ThreatByHangingPawn = S( 71, 61);
- const Score ThreatBySafePawn = S(182,175);
+ const Score ThreatBySafePawn = S(192,175);
const Score ThreatByRank = S( 16, 3);
const Score Hanging = S( 48, 27);
+ const Score WeakUnopposedPawn = S( 5, 25);
const Score ThreatByPawnPush = S( 38, 22);
const Score HinderPassedPawn = S( 7, 0);
-
- // Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
- // a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
- // happen in Chess960 games.
- const Score TrappedBishopA1H1 = S(50, 50);
+ const Score TrappedBishopA1H1 = S( 50, 50);
#undef S
#undef V
&& (pos.pieces(PAWN) & (s + pawn_push(Us))))
score += MinorBehindPawn;
- // Penalty for pawns on the same color square as the bishop
if (Pt == BISHOP)
+ {
+ // Penalty for pawns on the same color square as the bishop
score -= BishopPawns * pe->pawns_on_same_color_squares(Us, s);
+ // 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)))
+ score += LongRangedBishop;
+ }
+
// 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.
// evaluate_king() assigns bonuses and penalties to a king of a given color
- const Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB;
- const Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
- const Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
-
- const Bitboard KingFlank[FILE_NB] = {
- QueenSide, QueenSide, QueenSide, CenterFiles, CenterFiles, KingSide, KingSide, KingSide
- };
-
template<Tracing T> template<Color Us>
Score Evaluation<T>::evaluate_king() {
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Up = (Us == WHITE ? NORTH : SOUTH);
- const Bitboard Camp = (Us == WHITE ? ~Bitboard(0) ^ Rank6BB ^ Rank7BB ^ Rank8BB
- : ~Bitboard(0) ^ Rank1BB ^ Rank2BB ^ Rank3BB);
+ const Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB
+ : AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB);
const Square ksq = pos.square<KING>(Us);
- Bitboard undefended, b, b1, b2, safe, other;
+ Bitboard weak, b, b1, b2, safe, other;
int kingDanger;
// King shelter and enemy pawns storm
// Main king safety evaluation
if (kingAttackersCount[Them] > (1 - pos.count<QUEEN>(Them)))
{
- // Find the attacked squares which are defended only by our king...
- undefended = attackedBy[Them][ALL_PIECES]
- & attackedBy[Us][KING]
- & ~attackedBy2[Us];
-
- // ... and those which are not defended at all in the larger king ring
- b = attackedBy[Them][ALL_PIECES] & ~attackedBy[Us][ALL_PIECES]
- & kingRing[Us] & ~pos.pieces(Them);
+ // Attacked squares defended at most once by our queen or king
+ weak = attackedBy[Them][ALL_PIECES]
+ & ~attackedBy2[Us]
+ & (attackedBy[Us][KING] | attackedBy[Us][QUEEN] | ~attackedBy[Us][ALL_PIECES]);
// Initialize the 'kingDanger' variable, which will be transformed
// later into a king danger score. The initial value is based on the
// number and types of the enemy's attacking pieces, the number of
- // attacked and undefended squares around our king and the quality of
- // the pawn shelter (current 'score' value).
+ // attacked and weak squares around our king, the absence of queen and
+ // the quality of the pawn shelter (current 'score' value).
kingDanger = kingAttackersCount[Them] * kingAttackersWeight[Them]
+ 102 * kingAdjacentZoneAttacksCount[Them]
- + 201 * popcount(undefended)
- + 143 * (popcount(b) + !!pos.pinned_pieces(Us))
+ + 191 * popcount(kingRing[Us] & weak)
+ + 143 * !!pos.pinned_pieces(Us)
- 848 * !pos.count<QUEEN>(Them)
- 9 * mg_value(score) / 8
+ 40;
// Analyse the safe enemy's checks which are possible on next move
safe = ~pos.pieces(Them);
- safe &= ~attackedBy[Us][ALL_PIECES] | (undefended & attackedBy2[Them]);
+ safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]);
b1 = pos.attacks_from< ROOK>(ksq);
b2 = pos.attacks_from<BISHOP>(ksq);
// Enemy queen safe checks
- if ((b1 | b2) & attackedBy[Them][QUEEN] & safe)
+ if ((b1 | b2) & attackedBy[Them][QUEEN] & safe & ~attackedBy[Us][QUEEN])
kingDanger += QueenCheck;
- // For minors and rooks, also consider the square safe if attacked twice,
- // and only defended by our queen.
- safe |= attackedBy2[Them]
- & ~(attackedBy2[Us] | pos.pieces(Them))
- & attackedBy[Us][QUEEN];
-
// Some other potential checks are also analysed, even from squares
// currently occupied by the opponent own pieces, as long as the square
// is not attacked by our pawns, and is not occupied by a blocked pawn.
const Square Up = (Us == WHITE ? NORTH : SOUTH);
const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
- const Bitboard TRank2BB = (Us == WHITE ? Rank2BB : Rank7BB);
- const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
+ const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
Bitboard b, weak, defended, stronglyProtected, safeThreats;
Score score = SCORE_ZERO;
score += ThreatByKing[more_than_one(b)];
}
- // Bonus if some pawns can safely push and attack an enemy piece
- b = pos.pieces(Us, PAWN) & ~TRank7BB;
- b = shift<Up>(b | (shift<Up>(b & TRank2BB) & ~pos.pieces()));
+ // Bonus for opponent unopposed weak pawns
+ if (pos.pieces(Us, ROOK, QUEEN))
+ score += WeakUnopposedPawn * pe->weak_unopposed(Them);
- b &= ~pos.pieces()
- & ~attackedBy[Them][PAWN]
+ // 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]);
+ // Add a bonus for each new pawn threats from those squares
b = (shift<Left>(b) | shift<Right>(b))
& pos.pieces(Them)
& ~attackedBy[Us][PAWN];
}
- // evaluate_passer_pawns() evaluates the passed pawns and candidate passed
+ // evaluate_passed_pawns() evaluates the passed pawns and candidate passed
// pawns of the given color.
template<Tracing T> template<Color Us>
- Score Evaluation<T>::evaluate_passer_pawns() {
+ Score Evaluation<T>::evaluate_passed_pawns() {
const Color Them = (Us == WHITE ? BLACK : WHITE);
+ const Square Up = (Us == WHITE ? NORTH : SOUTH);
Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares;
Score score = SCORE_ZERO;
{
Square s = pop_lsb(&b);
- assert(!(pos.pieces(Them, PAWN) & forward_bb(Us, s + pawn_push(Us))));
+ assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up)));
- bb = forward_bb(Us, s) & (attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
+ bb = forward_file_bb(Us, s) & (attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
score -= HinderPassedPawn * popcount(bb);
int r = relative_rank(Us, s) - RANK_2;
if (rr)
{
- Square blockSq = s + pawn_push(Us);
+ Square blockSq = s + Up;
// Adjust bonus based on the king's proximity
ebonus += distance(pos.square<KING>(Them), blockSq) * 5 * rr
// If blockSq is not the queening square then consider also a second push
if (relative_rank(Us, blockSq) != RANK_8)
- ebonus -= distance(pos.square<KING>(Us), blockSq + pawn_push(Us)) * rr;
+ ebonus -= distance(pos.square<KING>(Us), blockSq + Up) * rr;
// 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_bb(Us, s);
+ defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s);
- bb = forward_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(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];
// 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 + pawn_push(Us)) || (pos.pieces(PAWN) & forward_bb(Us, s)))
+ if (!pos.pawn_passed(Us, s + Up) || (pos.pieces(PAWN) & forward_file_bb(Us, s)))
mbonus /= 2, ebonus /= 2;
score += make_score(mbonus, ebonus) + PassedFile[file_of(s)];
// that the endgame score will never change sign after the bonus.
int v = ((eg > 0) - (eg < 0)) * std::max(initiative, -abs(eg));
+ if (T)
+ Trace::add(INITIATIVE, make_score(0, v));
+
return make_score(0, v);
}
score += evaluate_threats<WHITE>()
- evaluate_threats<BLACK>();
- score += evaluate_passer_pawns<WHITE>()
- - evaluate_passer_pawns<BLACK>();
+ score += evaluate_passed_pawns<WHITE>()
+ - evaluate_passed_pawns<BLACK>();
if (pos.non_pawn_material() >= SpaceThreshold)
score += evaluate_space<WHITE>()
<< " Threats | " << Term(THREAT)
<< " Passed pawns | " << Term(PASSED)
<< " Space | " << Term(SPACE)
+ << " Initiative | " << Term(INITIATIVE)
<< "----------------+-------------+-------------+-------------\n"
<< " Total | " << Term(TOTAL);