int kingAdjacentZoneAttacksCount[COLOR_NB];
Bitboard pinnedPieces[COLOR_NB];
+ Material::Entry* me;
Pawns::Entry* pi;
};
enum { Mobility, PawnStructure, PassedPawns, Space, KingSafety };
const struct Weight { int mg, eg; } Weights[] = {
- {289, 344}, {233, 201}, {221, 273}, {46, 0}, {322, 0}
+ {266, 334}, {214, 203}, {193, 262}, {47, 0}, {330, 0}
};
Score operator*(Score s, const Weight& w) {
{ S(18, 5), S(27, 8) } // Bishops
};
+ // ReachableOutpost[knight/bishop][supported by pawn] contains bonuses for
+ // knights and bishops which can reach an 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.
const Score Threat[2][PIECE_TYPE_NB] = {
- { S(0, 0), S(0, 32), S(25, 39), S(28, 44), S(42, 98), S(35,105) }, // Minor attacks
- { S(0, 0), S(0, 27), S(26, 57), S(26, 57), S( 0, 30), S(23, 51) } // Rook attacks
+ { S(0, 0), S(0, 33), S(45, 43), S(46, 47), S(72, 107), S(48,118) }, // Minor attacks
+ { S(0, 0), S(0, 25), S(40, 62), S(40, 59), S( 0, 34), S(35, 48) } // Rook attacks
};
// ThreatenedByPawn[PieceType] contains a penalty according to which piece
// type is attacked by a pawn.
const Score ThreatenedByPawn[PIECE_TYPE_NB] = {
- S(0, 0), S(0, 0), S(107, 138), S(84, 122), S(114, 203), S(121, 217)
+ S(0, 0), S(0, 0), S(176, 139), S(131, 127), S(217, 218), S(203, 215)
};
- // Passed[mg/eg][rank] contains midgame and endgame bonuses for passed pawns.
+ // Passed[mg/eg][Rank] contains midgame and endgame bonuses for passed pawns.
// We don't use a Score because we process the two components independently.
const Value Passed[][RANK_NB] = {
{ V(0), V( 1), V(34), V(90), V(214), V(328) },
S(-27, -12), S( 1, -8), S( 3, 10), S( 12, 10)
};
- const Score ThreatenedByHangingPawn = S(40, 60);
+ const Score ThreatenedByHangingPawn = S(70, 63);
// Assorted bonuses and penalties used by evaluation
- const Score KingOnOne = S( 2, 58);
- const Score KingOnMany = S( 6,125);
+ const Score KingOnOne = S( 3, 62);
+ const Score KingOnMany = S( 9,138);
const Score RookOnPawn = S( 7, 27);
const Score RookOnOpenFile = S(43, 21);
const Score RookOnSemiOpenFile = S(19, 10);
const Score MinorBehindPawn = S(16, 0);
const Score TrappedRook = S(92, 0);
const Score Unstoppable = S( 0, 20);
- const Score Hanging = S(31, 26);
- const Score PawnAttackThreat = S(20, 20);
+ const Score Hanging = S(48, 28);
+ const Score PawnAttackThreat = S(31, 19);
const Score Checked = S(20, 20);
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
#undef S
#undef V
- // SpaceMask[Color] contains the area of the board which is considered
- // by the space evaluation. In the middlegame, each side is given a bonus
- // based on how many squares inside this area are safe and available for
- // friendly minor pieces.
- const Bitboard SpaceMask[COLOR_NB] = {
- (FileCBB | FileDBB | FileEBB | FileFBB) & (Rank2BB | Rank3BB | Rank4BB),
- (FileCBB | FileDBB | FileEBB | FileFBB) & (Rank7BB | Rank6BB | Rank5BB)
- };
-
// King danger constants and variables. The king danger scores are looked-up
// in KingDanger[]. Various little "meta-bonuses" measuring the strength
// of the enemy attack are added up into an integer, which is used as an
const int KnightCheck = 14;
- // init_eval_info() initializes king bitboards for given color adding
- // pawn attacks. To be done at the beginning of the evaluation.
+ // eval_init() initializes king and attack bitboards for given color
+ // adding pawn attacks. To be done at the beginning of the evaluation.
template<Color Us>
- void init_eval_info(const Position& pos, EvalInfo& ei) {
+ void eval_init(const Position& pos, EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Down = (Us == WHITE ? DELTA_S : DELTA_N);
}
- // evaluate_pieces() assigns bonuses and penalties to the pieces of a given color
-
- template<PieceType Pt, Color Us, bool DoTrace>
- Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility, const Bitboard* mobilityArea) {
+ // evaluate_pieces() assigns bonuses and penalties to the pieces of a given
+ // color and type.
- Bitboard b;
+ template<bool DoTrace, Color Us = WHITE, PieceType Pt = KNIGHT>
+ Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility,
+ const Bitboard* mobilityArea) {
+ Bitboard b, bb;
Square s;
Score score = SCORE_ZERO;
const PieceType NextPt = (Us == WHITE ? Pt : PieceType(Pt + 1));
const Color Them = (Us == WHITE ? BLACK : WHITE);
+ const Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
+ : Rank5BB | Rank4BB | Rank3BB);
const Square* pl = pos.squares<Pt>(Us);
ei.attackedBy[Us][Pt] = 0;
{
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 = OutpostRanks & ~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
Trace::add(Pt, Us, score);
// Recursively call evaluate_pieces() of next piece type until KING excluded
- return score - evaluate_pieces<NextPt, Them, DoTrace>(pos, ei, mobility, mobilityArea);
+ return score - evaluate_pieces<DoTrace, Them, NextPt>(pos, ei, mobility, mobilityArea);
}
template<>
- Score evaluate_pieces<KING, WHITE, false>(const Position&, EvalInfo&, Score*, const Bitboard*) { return SCORE_ZERO; }
+ Score evaluate_pieces<false, WHITE, KING>(const Position&, EvalInfo&, Score*, const Bitboard*) { return SCORE_ZERO; }
template<>
- Score evaluate_pieces<KING, WHITE, true>(const Position&, EvalInfo&, Score*, const Bitboard*) { return SCORE_ZERO; }
+ Score evaluate_pieces< true, WHITE, KING>(const Position&, EvalInfo&, Score*, const Bitboard*) { return SCORE_ZERO; }
// evaluate_king() assigns bonuses and penalties to a king of a given color
if (ei.kingAttackersCount[Them])
{
// Find the attacked squares around the king which have no defenders
- // apart from the king itself
+ // apart from the king itself.
undefended = ei.attackedBy[Them][ALL_PIECES]
& ei.attackedBy[Us][KING]
& ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
Score evaluate_space(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
+ const Bitboard SpaceMask =
+ Us == WHITE ? (FileCBB | FileDBB | FileEBB | FileFBB) & (Rank2BB | Rank3BB | Rank4BB)
+ : (FileCBB | FileDBB | FileEBB | FileFBB) & (Rank7BB | Rank6BB | Rank5BB);
// Find the safe squares for our pieces inside the area defined by
- // SpaceMask[]. A square is unsafe if it is attacked by an enemy
+ // SpaceMask. A square is unsafe if it is attacked by an enemy
// pawn, or if it is undefended and attacked by an enemy piece.
- Bitboard safe = SpaceMask[Us]
+ Bitboard safe = SpaceMask
& ~pos.pieces(Us, PAWN)
& ~ei.attackedBy[Them][PAWN]
& (ei.attackedBy[Us][ALL_PIECES] | ~ei.attackedBy[Them][ALL_PIECES]);
return make_score(0, value);
}
+
+ // evaluate_scale_factor() computes the scale factor for the winning side
+ ScaleFactor evaluate_scale_factor(const Position& pos, const EvalInfo& ei, Score score) {
+
+ Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK;
+ ScaleFactor sf = ei.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 ( ei.me->game_phase() < PHASE_MIDGAME
+ && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN))
+ {
+ if (pos.opposite_bishops())
+ {
+ // Endgame with opposite-colored bishops and no other pieces (ignoring pawns)
+ // is almost a draw, in case of KBP vs KB is even more a draw.
+ if ( pos.non_pawn_material(WHITE) == BishopValueMg
+ && pos.non_pawn_material(BLACK) == BishopValueMg)
+ sf = more_than_one(pos.pieces(PAWN)) ? ScaleFactor(31) : ScaleFactor(9);
+
+ // 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 = ScaleFactor(46 * sf / SCALE_FACTOR_NORMAL);
+ }
+ // Endings where weaker side can place his king in front of the opponent's
+ // pawns are drawish.
+ else if ( abs(eg_value(score)) <= BishopValueEg
+ && ei.pi->pawn_span(strongSide) <= 1
+ && !pos.pawn_passed(~strongSide, pos.square<KING>(~strongSide)))
+ sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(51) : ScaleFactor(37);
+ }
+
+ return sf;
+ }
+
} // namespace
/// evaluate() is the main evaluation function. It returns a static evaluation
-/// of the position always from the point of view of the side to move.
+/// of the position from the point of view of the side to move.
template<bool DoTrace>
Value Eval::evaluate(const Position& pos) {
EvalInfo ei;
Score score, mobility[2] = { SCORE_ZERO, SCORE_ZERO };
- // Initialize score by reading the incrementally updated scores included
- // in the position object (material + piece square tables).
- // Score is computed from the point of view of white.
+ // Initialize score by reading the incrementally updated scores included in
+ // the position object (material + piece square tables). Score is computed
+ // internally from the white point of view.
score = pos.psq_score();
// Probe the material hash table
- Material::Entry* me = Material::probe(pos);
- score += me->imbalance();
+ ei.me = Material::probe(pos);
+ score += ei.me->imbalance();
// If we have a specialized evaluation function for the current material
// configuration, call it and return.
- if (me->specialized_eval_exists())
- return me->evaluate(pos);
+ if (ei.me->specialized_eval_exists())
+ return ei.me->evaluate(pos);
// Probe the pawn hash table
ei.pi = Pawns::probe(pos);
// Initialize attack and king safety bitboards
ei.attackedBy[WHITE][ALL_PIECES] = ei.attackedBy[BLACK][ALL_PIECES] = 0;
- init_eval_info<WHITE>(pos, ei);
- init_eval_info<BLACK>(pos, ei);
+ eval_init<WHITE>(pos, ei);
+ eval_init<BLACK>(pos, ei);
- // Pawns blocked or on ranks 2 and 3. Will be excluded from the mobility area
+ // Pawns blocked or on ranks 2 and 3 will be excluded from the mobility area
Bitboard blockedPawns[] = {
pos.pieces(WHITE, PAWN) & (shift_bb<DELTA_S>(pos.pieces()) | Rank2BB | Rank3BB),
pos.pieces(BLACK, PAWN) & (shift_bb<DELTA_N>(pos.pieces()) | Rank7BB | Rank6BB)
};
- // Do not include in mobility squares protected by enemy pawns, or occupied
+ // Do not include in mobility area squares protected by enemy pawns, or occupied
// by our blocked pawns or king.
Bitboard mobilityArea[] = {
~(ei.attackedBy[BLACK][PAWN] | blockedPawns[WHITE] | pos.square<KING>(WHITE)),
~(ei.attackedBy[WHITE][PAWN] | blockedPawns[BLACK] | pos.square<KING>(BLACK))
};
- // Evaluate pieces and mobility
- score += evaluate_pieces<KNIGHT, WHITE, DoTrace>(pos, ei, mobility, mobilityArea);
+ // Evaluate all pieces but king and pawns
+ score += evaluate_pieces<DoTrace>(pos, ei, mobility, mobilityArea);
score += (mobility[WHITE] - mobility[BLACK]) * Weights[Mobility];
- // Evaluate kings after all other pieces because we need complete attack
+ // Evaluate kings after all other pieces because we need full attack
// information when computing the king safety evaluation.
score += evaluate_king<WHITE, DoTrace>(pos, ei)
- evaluate_king<BLACK, DoTrace>(pos, ei);
// Evaluate space for both sides, only during opening
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];
+ score += ( evaluate_space<WHITE>(pos, ei)
+ - evaluate_space<BLACK>(pos, ei)) * Weights[Space];
// 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;
- ScaleFactor 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 ( me->game_phase() < PHASE_MIDGAME
- && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN))
- {
- if (pos.opposite_bishops())
- {
- // Endgame with opposite-colored bishops and no other pieces (ignoring pawns)
- // is almost a draw, in case of KBP vs KB is even more a draw.
- if ( pos.non_pawn_material(WHITE) == BishopValueMg
- && pos.non_pawn_material(BLACK) == BishopValueMg)
- sf = more_than_one(pos.pieces(PAWN)) ? ScaleFactor(31) : ScaleFactor(9);
-
- // 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 = ScaleFactor(46 * sf / SCALE_FACTOR_NORMAL);
- }
- // Endings where weaker side can place his king in front of the opponent's
- // pawns are drawish.
- else if ( abs(eg_value(score)) <= BishopValueEg
- && ei.pi->pawn_span(strongSide) <= 1
- && !pos.pawn_passed(~strongSide, pos.square<KING>(~strongSide)))
- sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(51) : ScaleFactor(37);
- }
+ // Evaluate scale factor for the winning side
+ ScaleFactor sf = evaluate_scale_factor(pos, ei, score);
// Interpolate between a middlegame and a (scaled by 'sf') endgame score
- Value v = mg_value(score) * int(me->game_phase())
- + eg_value(score) * int(PHASE_MIDGAME - me->game_phase()) * sf / SCALE_FACTOR_NORMAL;
+ Value v = mg_value(score) * int(ei.me->game_phase())
+ + eg_value(score) * int(PHASE_MIDGAME - ei.me->game_phase()) * sf / SCALE_FACTOR_NORMAL;
v /= int(PHASE_MIDGAME);
- // In case of tracing add all single evaluation terms
+ // In case of tracing add all remaining individual evaluation terms
if (DoTrace)
{
Trace::add(MATERIAL, pos.psq_score());
- Trace::add(IMBALANCE, me->imbalance());
+ Trace::add(IMBALANCE, ei.me->imbalance());
Trace::add(PAWN, ei.pi->pawns_score());
Trace::add(MOBILITY, mobility[WHITE] * Weights[Mobility]
, mobility[BLACK] * Weights[Mobility]);