// Evaluation weights, indexed by the corresponding evaluation term
- enum { Mobility, PawnStructure, PassedPawns, Space, KingSafety };
+ enum { Mobility, PawnStructure, PassedPawns, Space, KingSafety, Threats };
const struct Weight { int mg, eg; } Weights[] = {
- {289, 344}, {233, 201}, {221, 273}, {46, 0}, {322, 0}
+ {289, 344}, {233, 201}, {221, 273}, {46, 0}, {322, 0}, {350, 256}
};
Score operator*(Score s, const Weight& w) {
S(112,125), S(113,127), S(117,137), S(122,143) }
};
+ // Mask of allowed outpost squares indexed by color
+ const Bitboard OutpostMask[COLOR_NB] = {
+ Rank4BB | Rank5BB | Rank6BB, Rank5BB | Rank4BB | Rank3BB
+ };
+
// Outpost[knight/bishop][supported by pawn] contains bonuses for knights and
// bishops outposts, bigger if outpost piece is supported by a pawn.
const Score Outpost[][2] = {
{ S(18, 5), S(27, 8) } // Bishops
};
+ // ReachableOutpost[knight/bishop][supported by pawn] contains bonuses for knights and
+ // bishops which can reach a outpost square in one move, bigger if outpost square is supported by a pawn.
+ const Score ReachableOutpost[][2] = {
+ { S(21, 5), S(31, 8) }, // Knights
+ { S( 8, 2), S(13, 4) } // Bishops
+ };
+
// Threat[minor/rook][attacked PieceType] contains
// bonuses according to which piece type attacks which one.
// Attacks on lesser pieces which are pawn defended are not considered.
// PassedFile[File] contains a bonus according to the file of a passed pawn.
const Score PassedFile[] = {
- S( 12, 10), S( 3, 10), S( 1, -8), S(-27, -12),
- S(-27, -12), S( 1, -8), S( 3, 10), S( 12, 10)
+ S( 12, 10), S( 3, 10), S( 1, -8), S(-27, -12),
+ S(-27, -12), S( 1, -8), S( 3, 10), S( 12, 10)
};
const Score ThreatenedByHangingPawn = S(40, 60);
template<PieceType Pt, Color Us, bool DoTrace>
Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility, const Bitboard* mobilityArea) {
- Bitboard b;
+ Bitboard b, bb;
Square s;
Score score = SCORE_ZERO;
{
ei.kingAttackersCount[Us]++;
ei.kingAttackersWeight[Us] += KingAttackWeights[Pt];
- Bitboard bb = b & ei.attackedBy[Them][KING];
+ bb = b & ei.attackedBy[Them][KING];
if (bb)
ei.kingAdjacentZoneAttacksCount[Us] += popcount<Max15>(bb);
}
if (Pt == BISHOP || Pt == KNIGHT)
{
- // Bonus for outpost square
- if ( relative_rank(Us, s) >= RANK_4
- && relative_rank(Us, s) <= RANK_6
- && !(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s)))
+ // Bonus for outpost squares
+ bb = OutpostMask[Us] & ~ei.pi->pawn_attacks_span(Them);
+ if (bb & s)
score += Outpost[Pt == BISHOP][!!(ei.attackedBy[Us][PAWN] & s)];
+ else
+ {
+ bb &= b & ~pos.pieces(Us);
+ if (bb)
+ score += ReachableOutpost[Pt == BISHOP][!!(ei.attackedBy[Us][PAWN] & bb)];
+ }
// Bonus when behind a pawn
if ( relative_rank(Us, s) < RANK_5
score += popcount<Max15>(b) * PawnAttackThreat;
if (DoTrace)
- Trace::add(THREAT, Us, score);
+ Trace::add(THREAT, Us, score * Weights[Threats]);
- return score;
+ return score * Weights[Threats];
}
// evaluate_initiative() computes the initiative correction value for the
// position, i.e. second order bonus/malus based on the known attacking/defending
// status of the players.
- Score evaluate_initiative(const Position& pos, const EvalInfo& ei, const Score positional_score) {
+ Score evaluate_initiative(const Position& pos, int asymmetry, Value eg) {
- int king_separation = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
- int pawns = pos.count<PAWN>(WHITE) + pos.count<PAWN>(BLACK);
- int asymmetry = ei.pi->pawn_asymmetry();
+ int kingDistance = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
+ int pawns = pos.count<PAWN>(WHITE) + pos.count<PAWN>(BLACK);
// Compute the initiative bonus for the attacking side
- int attacker_bonus = 8 * (pawns + asymmetry + king_separation) - 120;
+ int initiative = 8 * (pawns + asymmetry + kingDistance - 15);
- // Now apply the bonus: note that we find the attacking side by extracting the sign
- // of the endgame value of "positional_score", and that we carefully cap the bonus so
- // that the endgame score with the correction will never be divided by more than two.
- int eg = eg_value(positional_score);
- int value = ((eg > 0) - (eg < 0)) * std::max(attacker_bonus, -abs(eg / 2));
+ // 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 be divided by more than two.
+ int value = ((eg > 0) - (eg < 0)) * std::max(initiative, -abs(eg / 2));
return make_score(0, value);
}
if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >= 12222)
score += (evaluate_space<WHITE>(pos, ei) - evaluate_space<BLACK>(pos, ei)) * Weights[Space];
- // Evaluate initiative
- score += evaluate_initiative(pos, ei, score);
+ // Evaluate position potential for the winning side
+ score += evaluate_initiative(pos, ei.pi->pawn_asymmetry(), eg_value(score));
// Scale winning side if position is more drawish than it appears
Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK;