const int GrainSize = 8;
// Evaluation weights, initialized from UCI options
- enum { Mobility, PawnStructure, PassedPawns, Space, KingSafetyUs, KingSafetyThem };
+ enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem };
Score Weights[6];
typedef Value V;
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0) // 8
};
- // ThreatBonus[][] contains bonus according to which piece type
- // attacks which one.
- #define Z S(0, 0)
-
+ // ThreatBonus[attacking][attacked] contains bonus according to which
+ // piece type attacks which one.
const Score ThreatBonus[8][8] = {
- { Z, Z, Z, Z, Z, Z, Z, Z }, // not used
- { Z, S(18,37), Z, S(37,47), S(55,97), S(55,97), Z, Z }, // KNIGHT attacks
- { Z, S(18,37), S(37,47), Z, S(55,97), S(55,97), Z, Z }, // BISHOP attacks
- { Z, S( 9,27), S(27,47), S(27,47), Z, S(37,47), Z, Z }, // ROOK attacks
- { Z, S(27,37), S(27,37), S(27,37), S(27,37), Z, Z, Z }, // QUEEN attacks
- { Z, Z, Z, Z, Z, Z, Z, Z }, // not used
- { Z, Z, Z, Z, Z, Z, Z, Z }, // not used
- { Z, Z, Z, Z, Z, Z, Z, Z } // not used
+ {},
+ { S(0, 0), S(18,37), S( 0, 0), S(37,47), S(55,97), S(55,97) }, // KNIGHT
+ { S(0, 0), S(18,37), S(37,47), S( 0, 0), S(55,97), S(55,97) }, // BISHOP
+ { S(0, 0), S( 9,27), S(27,47), S(27,47), S( 0, 0), S(37,47) }, // ROOK
+ { S(0, 0), S(27,37), S(27,37), S(27,37), S(27,37), S( 0, 0) }, // QUEEN
+ {}, {}, {}
};
// ThreatedByPawnPenalty[] contains a penalty according to which piece
// type is attacked by an enemy pawn.
const Score ThreatedByPawnPenalty[8] = {
- Z, Z, S(56, 70), S(56, 70), S(76, 99), S(86, 118), Z, Z
+ S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118)
};
- #undef Z
#undef S
// Bonus for unstoppable passed pawns
(1ULL<<SQ_C5) | (1ULL<<SQ_D5) | (1ULL<<SQ_E5) | (1ULL<<SQ_F5)
};
- /// King safety constants and variables. The king safety scores are taken
- /// from the array SafetyTable[]. Various little "meta-bonuses" measuring
- /// the strength of the attack are added up into an integer, which is used
- /// as an index to SafetyTable[].
-
- // Attack weights for each piece type and table indexed on piece type
- const int QueenAttackWeight = 5;
- const int RookAttackWeight = 3;
- const int BishopAttackWeight = 2;
- const int KnightAttackWeight = 2;
+ /// King danger constants and variables. The king danger scores are taken
+ /// from the KingDangerTable[]. Various little "meta-bonuses" measuring
+ /// the strength of the enemy attack are added up into an integer, which
+ /// is used as an index to KingDangerTable[].
- const int AttackWeight[] = { 0, 0, KnightAttackWeight, BishopAttackWeight, RookAttackWeight, QueenAttackWeight };
+ // KingAttackWeights[] contains king attack weights by piece type
+ const int KingAttackWeights[8] = { 0, 0, 2, 2, 3, 5 };
- // Bonuses for safe checks
+ // Bonuses for enemy's safe checks
const int QueenContactCheckBonus = 3;
const int DiscoveredCheckBonus = 3;
const int QueenCheckBonus = 2;
15, 15, 15, 15, 15, 15, 15, 15
};
- // SafetyTable[color][] contains the actual king safety weighted scores
- Score SafetyTable[2][128];
+ // KingDangerTable[color][] contains the actual king danger weighted scores
+ Score KingDangerTable[2][128];
// Pawn and material hash tables, indexed by the current thread id.
// Note that they will be initialized at 0 being global variables.
void read_weights(Color us) {
- // King safety is asymmetrical. Our king safety is controled by "Cowardice"
- // UCI parameter, instead the opponent one by "Aggressiveness".
- const int kingSafetyUs = (us == WHITE ? KingSafetyUs : KingSafetyThem);
- const int kingSafetyThem = (us == WHITE ? KingSafetyThem : KingSafetyUs);
+ // King safety is asymmetrical. Our king danger level is weighted by
+ // "Cowardice" UCI parameter, instead the opponent one by "Aggressiveness".
+ const int kingDangerUs = (us == WHITE ? KingDangerUs : KingDangerThem);
+ const int kingDangerThem = (us == WHITE ? KingDangerThem : KingDangerUs);
Weights[Mobility] = weight_option("Mobility (Middle Game)", "Mobility (Endgame)", WeightsInternal[Mobility]);
Weights[PawnStructure] = weight_option("Pawn Structure (Middle Game)", "Pawn Structure (Endgame)", WeightsInternal[PawnStructure]);
Weights[PassedPawns] = weight_option("Passed Pawns (Middle Game)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]);
Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]);
- Weights[kingSafetyUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingSafetyUs]);
- Weights[kingSafetyThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingSafetyThem]);
+ Weights[kingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]);
+ Weights[kingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]);
// If running in analysis mode, make sure we use symmetrical king safety. We do this
- // by replacing both Weights[kingSafetyUs] and Weights[kingSafetyThem] by their average.
+ // by replacing both Weights[kingDangerUs] and Weights[kingDangerThem] by their average.
if (get_option_value_bool("UCI_AnalyseMode"))
- Weights[kingSafetyUs] = Weights[kingSafetyThem] = (Weights[kingSafetyUs] + Weights[kingSafetyThem]) / 2;
+ Weights[kingDangerUs] = Weights[kingDangerThem] = (Weights[kingDangerUs] + Weights[kingDangerThem]) / 2;
init_safety();
}
if (b & ei.kingZone[Us])
{
ei.kingAttackersCount[Us]++;
- ei.kingAttackersWeight[Us] += AttackWeight[Piece];
+ ei.kingAttackersWeight[Us] += KingAttackWeights[Piece];
Bitboard bb = (b & ei.attackedBy[Them][KING]);
if (bb)
ei.kingAdjacentZoneAttacksCount[Us] += count_1s_max_15<HasPopCnt>(bb);
const Color Them = (Us == WHITE ? BLACK : WHITE);
- Bitboard undefended, attackedByOthers, escapeSquares, occ, b, b2, safe;
+ Bitboard undefended, attackedByOthers, escapeSquares, occ, b, b1, b2, safe;
Square from, to;
bool sente;
- int attackUnits, count, shelter = 0;
- const Square s = pos.king_square(Us);
+ int attackUnits, shelter = 0;
+ const Square ksq = pos.king_square(Us);
// King shelter
- if (relative_rank(Us, s) <= RANK_4)
+ if (relative_rank(Us, ksq) <= RANK_4)
{
- shelter = ei.pi->get_king_shelter(pos, Us, s);
+ shelter = ei.pi->get_king_shelter(pos, Us, ksq);
ei.value += Sign[Us] * make_score(shelter, 0);
}
&& pos.non_pawn_material(Them) >= QueenValueMidgame + RookValueMidgame
&& ei.kingAdjacentZoneAttacksCount[Them])
{
- // Is it the attackers turn to move?
- sente = (Them == pos.side_to_move());
-
- // 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));
-
- // Initialize the 'attackUnits' variable, which is used later on as an
- // index to the SafetyTable[] array. The initial value is based on the
- // number and types of the attacking pieces, the number of attacked and
- // undefended squares around the king, the square of the king, and the
- // quality of the pawn shelter.
- attackUnits = Min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
- + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + count_1s_max_15<HasPopCnt>(undefended))
- + InitKingDanger[relative_square(Us, s)]
- - (shelter >> 5);
-
- // Analyse safe queen contact checks
- b = undefended & ei.attacked_by(Them, QUEEN) & ~pos.pieces_of_color(Them);
- if (b)
- {
- attackedByOthers = ei.attacked_by(Them, PAWN) | ei.attacked_by(Them, KNIGHT)
- | ei.attacked_by(Them, BISHOP) | ei.attacked_by(Them, ROOK);
-
- b &= attackedByOthers;
-
- // Squares attacked by the queen and supported by another enemy piece and
- // not defended by other pieces but our king.
+ // Is it the attackers turn to move?
+ sente = (Them == pos.side_to_move());
+
+ // 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));
+
+ // Initialize the 'attackUnits' variable, which is used later on as an
+ // index to the KingDangerTable[] array. 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, the square of the
+ // king, and the quality of the pawn shelter.
+ attackUnits = Min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
+ + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + count_1s_max_15<HasPopCnt>(undefended))
+ + InitKingDanger[relative_square(Us, ksq)]
+ - shelter / 32;
+
+ // Analyse safe queen contact checks
+ b = undefended & ei.attacked_by(Them, QUEEN) & ~pos.pieces_of_color(Them);
if (b)
{
- // The bitboard b now contains the squares available for safe queen
- // contact checks.
- count = count_1s_max_15<HasPopCnt>(b);
- attackUnits += QueenContactCheckBonus * count * (sente ? 2 : 1);
+ attackedByOthers = ei.attacked_by(Them, PAWN) | ei.attacked_by(Them, KNIGHT)
+ | ei.attacked_by(Them, BISHOP) | ei.attacked_by(Them, ROOK);
+
+ b &= attackedByOthers;
- // Is there a mate threat?
- if (QueenContactMates && !pos.is_check())
+ // Squares attacked by the queen and supported by another enemy piece and
+ // not defended by other pieces but our king.
+ if (b)
{
- escapeSquares = pos.attacks_from<KING>(s) & ~pos.pieces_of_color(Us) & ~attackedByOthers;
- occ = pos.occupied_squares();
- while (b)
- {
- to = pop_1st_bit(&b);
+ // The bitboard b now contains the squares available for safe queen
+ // contact checks.
+ attackUnits += QueenContactCheckBonus * count_1s_max_15<HasPopCnt>(b) * (sente ? 2 : 1);
- // Do we have escape squares from queen contact check attack ?
- if (!(escapeSquares & ~queen_attacks_bb(to, occ & ClearMaskBB[s])))
+ // Is there a mate threat?
+ if (QueenContactMates && !pos.is_check())
+ {
+ escapeSquares = pos.attacks_from<KING>(ksq) & ~pos.pieces_of_color(Us) & ~attackedByOthers;
+ occ = pos.occupied_squares();
+ while (b)
{
- // We have a mate, unless the queen is pinned or there
- // is an X-ray attack through the queen.
- for (int i = 0; i < pos.piece_count(Them, QUEEN); i++)
+ to = pop_1st_bit(&b);
+
+ // Do we have escape squares from queen contact check attack ?
+ if (!(escapeSquares & ~queen_attacks_bb(to, occ & ClearMaskBB[ksq])))
{
- from = pos.piece_list(Them, QUEEN, i);
- if ( bit_is_set(pos.attacks_from<QUEEN>(from), to)
- && !bit_is_set(pos.pinned_pieces(Them), from)
- && !(rook_attacks_bb(to, occ & ClearMaskBB[from]) & pos.pieces(ROOK, QUEEN, Us))
- && !(bishop_attacks_bb(to, occ & ClearMaskBB[from]) & pos.pieces(BISHOP, QUEEN, Us)))
-
- // Set the mate threat move
- ei.mateThreat[Them] = make_move(from, to);
+ // We have a mate, unless the queen is pinned or there
+ // is an X-ray attack through the queen.
+ for (int i = 0; i < pos.piece_count(Them, QUEEN); i++)
+ {
+ from = pos.piece_list(Them, QUEEN, i);
+ if ( bit_is_set(pos.attacks_from<QUEEN>(from), to)
+ && !bit_is_set(pos.pinned_pieces(Them), from)
+ && !(rook_attacks_bb(to, occ & ClearMaskBB[from]) & pos.pieces(ROOK, QUEEN, Us))
+ && !(bishop_attacks_bb(to, occ & ClearMaskBB[from]) & pos.pieces(BISHOP, QUEEN, Us)))
+
+ // Set the mate threat move
+ ei.mateThreat[Them] = make_move(from, to);
+ }
}
}
}
}
}
- }
- // Analyse safe distance checks
- safe = ~(pos.pieces_of_color(Them) | ei.attacked_by(Us));
+ // Analyse enemy's safe distance checks
+ safe = ~(pos.pieces_of_color(Them) | ei.attacked_by(Us));
- if (QueenCheckBonus > 0 || RookCheckBonus > 0)
- {
- b = pos.attacks_from<ROOK>(s) & safe;
-
- // Queen checks
- b2 = b & ei.attacked_by(Them, QUEEN);
- if (b2)
- attackUnits += QueenCheckBonus * count_1s_max_15<HasPopCnt>(b2);
+ b1 = pos.attacks_from<ROOK>(ksq) & safe;
+ b2 = pos.attacks_from<BISHOP>(ksq) & safe;
- // Rook checks
- b2 = b & ei.attacked_by(Them, ROOK);
- if (b2)
- attackUnits += RookCheckBonus * count_1s_max_15<HasPopCnt>(b2);
- }
- if (QueenCheckBonus > 0 || BishopCheckBonus > 0)
- {
- b = pos.attacks_from<BISHOP>(s) & safe;
-
- // Queen checks
- b2 = b & ei.attacked_by(Them, QUEEN);
- if (b2)
- attackUnits += QueenCheckBonus * count_1s_max_15<HasPopCnt>(b2);
+ // Enemy rooks safe checks
+ b = b1 & ei.attacked_by(Them, ROOK);
+ if (b)
+ attackUnits += RookCheckBonus * count_1s_max_15<HasPopCnt>(b);
- // Bishop checks
- b2 = b & ei.attacked_by(Them, BISHOP);
- if (b2)
- attackUnits += BishopCheckBonus * count_1s_max_15<HasPopCnt>(b2);
- }
- if (KnightCheckBonus > 0)
- {
- b = pos.attacks_from<KNIGHT>(s) & safe;
+ // Enemy bishops safe checks
+ b = b2 & ei.attacked_by(Them, BISHOP);
+ if (b)
+ attackUnits += BishopCheckBonus * count_1s_max_15<HasPopCnt>(b);
- // Knight checks
- b2 = b & ei.attacked_by(Them, KNIGHT);
- if (b2)
- attackUnits += KnightCheckBonus * count_1s_max_15<HasPopCnt>(b2);
- }
+ // Enemy queens safe checks
+ b = (b1 | b2) & ei.attacked_by(Them, QUEEN);
+ if (b)
+ attackUnits += QueenCheckBonus * count_1s_max_15<HasPopCnt>(b);
- // Analyse discovered checks (only for non-pawns right now, consider
- // adding pawns later).
- if (DiscoveredCheckBonus)
- {
- b = pos.discovered_check_candidates(Them) & ~pos.pieces(PAWN);
- if (b)
- attackUnits += DiscoveredCheckBonus * count_1s_max_15<HasPopCnt>(b) * (sente ? 2 : 1);
- }
+ // Enemy knights safe checks
+ b = pos.attacks_from<KNIGHT>(ksq) & ei.attacked_by(Them, KNIGHT) & safe;
+ if (b)
+ attackUnits += KnightCheckBonus * count_1s_max_15<HasPopCnt>(b);
- // Has a mate threat been found? We don't do anything here if the
- // side with the mating move is the side to move, because in that
- // case the mating side will get a huge bonus at the end of the main
- // evaluation function instead.
- if (ei.mateThreat[Them] != MOVE_NONE)
- attackUnits += MateThreatBonus;
-
- // Ensure that attackUnits is between 0 and 99, in order to avoid array
- // out of bounds errors.
- attackUnits = Min(99, Max(0, attackUnits));
-
- // Finally, extract the king safety score from the SafetyTable[] array.
- // Subtract the score from evaluation, and set ei.futilityMargin[].
- // The reason for storing the king safety score to futility margin
- // is that the king safety scores can sometimes be very big, and that
- // 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] * SafetyTable[Us][attackUnits];
- ei.futilityMargin[Us] = mg_value(SafetyTable[Us][attackUnits]);
+ // Analyse discovered checks (only for non-pawns right now, consider
+ // adding pawns later).
+ b = pos.discovered_check_candidates(Them) & ~pos.pieces(PAWN);
+ if (b)
+ attackUnits += DiscoveredCheckBonus * count_1s_max_15<HasPopCnt>(b) * (sente ? 2 : 1);
+
+ // Has a mate threat been found? We don't do anything here if the
+ // side with the mating move is the side to move, because in that
+ // case the mating side will get a huge bonus at the end of the main
+ // evaluation function instead.
+ if (ei.mateThreat[Them] != MOVE_NONE)
+ attackUnits += MateThreatBonus;
+
+ // Ensure that attackUnits is between 0 and 99, in order to avoid array
+ // out of bounds errors.
+ attackUnits = Min(99, Max(0, attackUnits));
+
+ // Finally, extract the king danger score from the KingDangerTable[]
+ // array and subtract the score from evaluation. Set also ei.kingDanger[]
+ // value that will be used for pruning because this value can sometimes
+ // 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.kingDanger[Us] = mg_value(KingDangerTable[Us][attackUnits]);
}
}
t[i] = Value(peak);
}
- // Then apply the weights and get the final SafetyTable[] array
+ // Then apply the weights and get the final KingDangerTable[] array
for (Color c = WHITE; c <= BLACK; c++)
for (int i = 0; i < 100; i++)
- SafetyTable[c][i] = apply_weight(make_score(t[i], 0), Weights[KingSafetyUs + c]);
+ KingDangerTable[c][i] = apply_weight(make_score(t[i], 0), Weights[KingDangerUs + c]);
}
}