namespace {
+ // Struct EvalInfo contains various information computed and collected
+ // by the evaluation functions.
+ struct EvalInfo {
+
+ // Middle and end game position's static evaluations
+ Score value;
+
+ // Pointer to pawn hash table entry
+ PawnInfo* pi;
+
+ // attackedBy[color][piece type] is a bitboard representing all squares
+ // attacked by a given color and piece type, attackedBy[color][0] contains
+ // all squares attacked by the given color.
+ Bitboard attackedBy[2][8];
+
+ // kingZone[color] is the zone around the enemy king which is considered
+ // by the king safety evaluation. This consists of the squares directly
+ // adjacent to the king, and the three (or two, for a king on an edge file)
+ // squares two ranks in front of the king. For instance, if black's king
+ // is on g8, kingZone[WHITE] is a bitboard containing the squares f8, h8,
+ // f7, g7, h7, f6, g6 and h6.
+ Bitboard kingZone[2];
+
+ // kingAttackersCount[color] is the number of pieces of the given color
+ // which attack a square in the kingZone of the enemy king.
+ int kingAttackersCount[2];
+
+ // kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
+ // given color which attack a square in the kingZone of the enemy king. The
+ // weights of the individual piece types are given by the variables
+ // QueenAttackWeight, RookAttackWeight, BishopAttackWeight and
+ // KnightAttackWeight in evaluate.cpp
+ int kingAttackersWeight[2];
+
+ // kingAdjacentZoneAttacksCount[color] is the number of attacks to squares
+ // directly adjacent to the king of the given color. Pieces which attack
+ // more than one square are counted multiple times. For instance, if black's
+ // king is on g8 and there's a white knight on g5, this knight adds
+ // 2 to kingAdjacentZoneAttacksCount[BLACK].
+ int kingAdjacentZoneAttacksCount[2];
+ };
+
const int Sign[2] = { 1, -1 };
// Evaluation grain size, must be a power of 2
// Function prototypes
template<bool HasPopCnt>
- Value do_evaluate(const Position& pos, EvalInfo& ei);
+ Value do_evaluate(const Position& pos, Value margins[]);
template<Color Us, bool HasPopCnt>
void init_attack_tables(const Position& pos, EvalInfo& ei);
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei);
template<Color Us, bool HasPopCnt>
- void evaluate_king(const Position& pos, EvalInfo& ei);
+ void evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]);
template<Color Us>
void evaluate_threats(const Position& pos, EvalInfo& ei);
/// evaluate() is the main evaluation function. It always computes two
/// values, an endgame score and a middle game score, and interpolates
/// between them based on the remaining material.
-Value evaluate(const Position& pos, EvalInfo& ei) {
+Value evaluate(const Position& pos, Value margins[]) {
- return CpuHasPOPCNT ? do_evaluate<true>(pos, ei)
- : do_evaluate<false>(pos, ei);
+ return CpuHasPOPCNT ? do_evaluate<true>(pos, margins)
+ : do_evaluate<false>(pos, margins);
}
namespace {
template<bool HasPopCnt>
-Value do_evaluate(const Position& pos, EvalInfo& ei) {
+Value do_evaluate(const Position& pos, Value margins[]) {
+ EvalInfo ei;
ScaleFactor factor[2];
Score mobility;
// position object (material + piece square tables).
ei.value = pos.value();
+ // margins[color] stores the uncertainty estimation of position's evaluation
+ // and typically is used by the search for pruning decisions.
+ margins[WHITE] = margins[BLACK] = VALUE_ZERO;
+
// Probe the material hash table
- ei.mi = MaterialTable[pos.thread()]->get_material_info(pos);
- ei.value += ei.mi->material_value();
+ MaterialInfo* mi = MaterialTable[pos.thread()]->get_material_info(pos);
+ ei.value += mi->material_value();
// If we have a specialized evaluation function for the current material
// configuration, call it and return.
- if (ei.mi->specialized_eval_exists())
- return ei.mi->evaluate(pos);
+ if (mi->specialized_eval_exists())
+ return mi->evaluate(pos);
// After get_material_info() call that modifies them
- factor[WHITE] = ei.mi->scale_factor(pos, WHITE);
- factor[BLACK] = ei.mi->scale_factor(pos, BLACK);
+ factor[WHITE] = mi->scale_factor(pos, WHITE);
+ factor[BLACK] = mi->scale_factor(pos, BLACK);
// Probe the pawn hash table
ei.pi = PawnTable[pos.thread()]->get_pawn_info(pos);
// Kings. Kings are evaluated after all other pieces for both sides,
// because we need complete attack information for all pieces when computing
// the king safety evaluation.
- evaluate_king<WHITE, HasPopCnt>(pos, ei);
- evaluate_king<BLACK, HasPopCnt>(pos, ei);
+ evaluate_king<WHITE, HasPopCnt>(pos, ei, margins);
+ evaluate_king<BLACK, HasPopCnt>(pos, ei, margins);
// Evaluate tactical threats, we need full attack info including king
evaluate_threats<WHITE>(pos, ei);
evaluate_passed_pawns<WHITE>(pos, ei);
evaluate_passed_pawns<BLACK>(pos, ei);
- Phase phase = ei.mi->game_phase();
+ Phase phase = mi->game_phase();
// Middle-game specific evaluation terms
if (phase > PHASE_ENDGAME)
ei.value += make_score(ei.pi->kingside_storm_value(WHITE) - ei.pi->queenside_storm_value(BLACK), 0);
// Evaluate space for both sides
- if (ei.mi->space_weight() > 0)
+ if (mi->space_weight() > 0)
{
int s = evaluate_space<WHITE, HasPopCnt>(pos, ei) - evaluate_space<BLACK, HasPopCnt>(pos, ei);
- ei.value += apply_weight(make_score(s * ei.mi->space_weight(), 0), Weights[Space]);
+ ei.value += apply_weight(make_score(s * mi->space_weight(), 0), Weights[Space]);
}
}
// evaluate_king<>() assigns bonuses and penalties to a king of a given color
template<Color Us, bool HasPopCnt>
- void evaluate_king(const Position& pos, EvalInfo& ei) {
+ void evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
// Find the attacked squares around the king which has no defenders
// apart from the king itself
- undefended = ei.attacked_by(Them) & ei.attacked_by(Us, KING);
- undefended &= ~( ei.attacked_by(Us, PAWN) | ei.attacked_by(Us, KNIGHT)
- | ei.attacked_by(Us, BISHOP) | ei.attacked_by(Us, ROOK)
- | ei.attacked_by(Us, QUEEN));
+ undefended = ei.attackedBy[Them][0] & ei.attackedBy[Us][KING];
+ undefended &= ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
+ | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
+ | ei.attackedBy[Us][QUEEN]);
// Initialize the 'attackUnits' variable, which is used later on as an
// index to the KingDangerTable[] array. The initial value is based on
// Analyse enemy's safe queen contact checks. First find undefended
// squares around the king attacked by enemy queen...
- b = undefended & ei.attacked_by(Them, QUEEN) & ~pos.pieces_of_color(Them);
+ b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces_of_color(Them);
if (b)
{
// ...then remove squares not supported by another enemy piece
- b &= ( ei.attacked_by(Them, PAWN) | ei.attacked_by(Them, KNIGHT)
- | ei.attacked_by(Them, BISHOP) | ei.attacked_by(Them, ROOK));
+ b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT]
+ | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]);
if (b)
attackUnits += QueenContactCheckBonus * count_1s_max_15<HasPopCnt>(b) * (sente ? 2 : 1);
}
// Analyse enemy's safe distance checks for sliders and knights
- safe = ~(pos.pieces_of_color(Them) | ei.attacked_by(Us));
+ safe = ~(pos.pieces_of_color(Them) | ei.attackedBy[Us][0]);
b1 = pos.attacks_from<ROOK>(ksq) & safe;
b2 = pos.attacks_from<BISHOP>(ksq) & safe;
// Enemy queen safe checks
- b = (b1 | b2) & ei.attacked_by(Them, QUEEN);
+ b = (b1 | b2) & ei.attackedBy[Them][QUEEN];
if (b)
attackUnits += QueenCheckBonus * count_1s_max_15<HasPopCnt>(b);
// Enemy rooks safe checks
- b = b1 & ei.attacked_by(Them, ROOK);
+ b = b1 & ei.attackedBy[Them][ROOK];
if (b)
attackUnits += RookCheckBonus * count_1s_max_15<HasPopCnt>(b);
// Enemy bishops safe checks
- b = b2 & ei.attacked_by(Them, BISHOP);
+ b = b2 & ei.attackedBy[Them][BISHOP];
if (b)
attackUnits += BishopCheckBonus * count_1s_max_15<HasPopCnt>(b);
// Enemy knights safe checks
- b = pos.attacks_from<KNIGHT>(ksq) & ei.attacked_by(Them, KNIGHT) & safe;
+ b = pos.attacks_from<KNIGHT>(ksq) & ei.attackedBy[Them][KNIGHT] & safe;
if (b)
attackUnits += KnightCheckBonus * count_1s_max_15<HasPopCnt>(b);
// be very big, and so capturing a single attacking piece can therefore
// result in a score change far bigger than the value of the captured piece.
ei.value -= Sign[Us] * KingDangerTable[Us][attackUnits];
- ei.margin[Us] = mg_value(KingDangerTable[Us][attackUnits]);
- } else
- ei.margin[Us] = VALUE_ZERO;
+ margins[Us] += mg_value(KingDangerTable[Us][attackUnits]);
+ }
}
if (pos.square_is_empty(blockSq))
{
squaresToQueen = squares_in_front_of(Us, s);
- defendedSquares = squaresToQueen & ei.attacked_by(Us);
+ defendedSquares = squaresToQueen & ei.attackedBy[Us][0];
// If there is an enemy rook or queen attacking the pawn from behind,
// add all X-ray attacks by the rook or queen. Otherwise consider only
&& (squares_behind(Us, s) & pos.pieces(ROOK, QUEEN, Them) & pos.attacks_from<ROOK>(s)))
unsafeSquares = squaresToQueen;
else
- unsafeSquares = squaresToQueen & (ei.attacked_by(Them) | pos.pieces_of_color(Them));
+ unsafeSquares = squaresToQueen & (ei.attackedBy[Them][0] | pos.pieces_of_color(Them));
// If there aren't enemy attacks or pieces along the path to queen give
// huge bonus. Even bigger if we protect the pawn's path.
// pawn, or if it is undefended and attacked by an enemy piece.
Bitboard safe = SpaceMask[Us]
& ~pos.pieces(PAWN, Us)
- & ~ei.attacked_by(Them, PAWN)
- & (ei.attacked_by(Us) | ~ei.attacked_by(Them));
+ & ~ei.attackedBy[Them][PAWN]
+ & (ei.attackedBy[Us][0] | ~ei.attackedBy[Them][0]);
// Find all squares which are at most three squares behind some friendly pawn
Bitboard behind = pos.pieces(PAWN, Us);