Skip to content
Permalink
Browse files

Always initialize and evaluate king safety

Recent tests by @xoto10, @Vizvezdenec, and myself seemed to hint that Elo could
be gained by expanding the number of cases where king safety is applied. Several
users (@Spliffjiffer, @Vizvezdenec) have anticipated benefits specifically in
evaluation of tactics. It appears that we actually do not need to restrict the
cases in which we initialize and evaluate king safety at all: initializing and
evaluating it in every position appears roughly Elo-neutral at STC and possibly
a substantial Elo gain at LTC.

Any explanation for this scaling is, at this point, conjecture. Assuming it is
not due to chance, my hypothesis is that initialization of king safety in all
positions is a mild slowdown, offset by an Elo gain of evaluating king safety
in all positions. At STC this produces Elo gains and losses that offset each
other, while at longer time control the slowdown is much less important, leaving
only the Elo gain. It probably helps SF to explore king attacks much earlier in
search with high numbers of enemy pieces concentrating but not essentially attacking
king ring.

Thanks to @xoto10 and @Vizvezdenec for helping run my LTC!

Closes official-stockfish#1906

STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 35432 W: 7815 L: 7721 D: 19896
http://tests.stockfishchess.org/tests/view/5c24779d0ebc5902ba131b26

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 12887 W: 2217 L: 2084 D: 8586
http://tests.stockfishchess.org/tests/view/5c25049a0ebc5902ba132586

Bench: 3163951

------------------

How to continue from there?

* Next step will be to tune all the king danger terms once more after that :-)
  • Loading branch information...
31m059 authored and snicolet committed Dec 27, 2018
1 parent 14c4a40 commit 69dc5568b3bb9a02c71bdd5bb0e87135e892ea19
Showing with 61 additions and 69 deletions.
  1. +61 −69 src/evaluate.cpp
@@ -258,25 +258,20 @@ namespace {
attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
attackedBy2[Us] = attackedBy[Us][KING] & attackedBy[Us][PAWN];

kingRing[Us] = kingAttackersCount[Them] = 0;
// Init our king safety tables
kingRing[Us] = attackedBy[Us][KING];
if (relative_rank(Us, pos.square<KING>(Us)) == RANK_1)
kingRing[Us] |= shift<Up>(kingRing[Us]);

// Init our king safety tables only if we are going to use them
if (pos.non_pawn_material(Them) >= RookValueMg + KnightValueMg)
{
kingRing[Us] = attackedBy[Us][KING];
if (relative_rank(Us, pos.square<KING>(Us)) == RANK_1)
kingRing[Us] |= shift<Up>(kingRing[Us]);

if (file_of(pos.square<KING>(Us)) == FILE_H)
kingRing[Us] |= shift<WEST>(kingRing[Us]);
if (file_of(pos.square<KING>(Us)) == FILE_H)
kingRing[Us] |= shift<WEST>(kingRing[Us]);

else if (file_of(pos.square<KING>(Us)) == FILE_A)
kingRing[Us] |= shift<EAST>(kingRing[Us]);
else if (file_of(pos.square<KING>(Us)) == FILE_A)
kingRing[Us] |= shift<EAST>(kingRing[Us]);

kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
kingRing[Us] &= ~double_pawn_attacks_bb<Us>(pos.pieces(Us, PAWN));
kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
}
kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
kingRing[Us] &= ~double_pawn_attacks_bb<Us>(pos.pieces(Us, PAWN));
kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
}


@@ -424,67 +419,64 @@ namespace {
int tropism = popcount(b1) + popcount(b2);

// Main king safety evaluation
if (kingAttackersCount[Them] > 1 - pos.count<QUEEN>(Them))
{
int kingDanger = 0;
unsafeChecks = 0;
int kingDanger = 0;
unsafeChecks = 0;

// Attacked squares defended at most once by our queen or king
weak = attackedBy[Them][ALL_PIECES]
& ~attackedBy2[Us]
& (~attackedBy[Us][ALL_PIECES] | attackedBy[Us][KING] | attackedBy[Us][QUEEN]);
// Attacked squares defended at most once by our queen or king
weak = attackedBy[Them][ALL_PIECES]
& ~attackedBy2[Us]
& (~attackedBy[Us][ALL_PIECES] | attackedBy[Us][KING] | attackedBy[Us][QUEEN]);

// Analyse the safe enemy's checks which are possible on next move
safe = ~pos.pieces(Them);
safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]);
// Analyse the safe enemy's checks which are possible on next move
safe = ~pos.pieces(Them);
safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]);

b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));

// Enemy queen safe checks
if ((b1 | b2) & attackedBy[Them][QUEEN] & safe & ~attackedBy[Us][QUEEN])
kingDanger += QueenSafeCheck;
// Enemy queen safe checks
if ((b1 | b2) & attackedBy[Them][QUEEN] & safe & ~attackedBy[Us][QUEEN])
kingDanger += QueenSafeCheck;

b1 &= attackedBy[Them][ROOK];
b2 &= attackedBy[Them][BISHOP];
b1 &= attackedBy[Them][ROOK];
b2 &= attackedBy[Them][BISHOP];

// Enemy rooks checks
if (b1 & safe)
kingDanger += RookSafeCheck;
else
unsafeChecks |= b1;
// Enemy rooks checks
if (b1 & safe)
kingDanger += RookSafeCheck;
else
unsafeChecks |= b1;

// Enemy bishops checks
if (b2 & safe)
kingDanger += BishopSafeCheck;
else
unsafeChecks |= b2;
// Enemy bishops checks
if (b2 & safe)
kingDanger += BishopSafeCheck;
else
unsafeChecks |= b2;

// Enemy knights checks
b = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
if (b & safe)
kingDanger += KnightSafeCheck;
else
unsafeChecks |= b;

// Unsafe or occupied checking squares will also be considered, as long as
// the square is in the attacker's mobility area.
unsafeChecks &= mobilityArea[Them];

kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
+ 69 * kingAttacksCount[Them]
+ 185 * popcount(kingRing[Us] & weak)
+ 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks)
+ tropism * tropism / 4
- 873 * !pos.count<QUEEN>(Them)
- 6 * mg_value(score) / 8
+ mg_value(mobility[Them] - mobility[Us])
- 30;

// Transform the kingDanger units into a Score, and subtract it from the evaluation
if (kingDanger > 0)
score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);
}
// Enemy knights checks
b = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
if (b & safe)
kingDanger += KnightSafeCheck;
else
unsafeChecks |= b;

// Unsafe or occupied checking squares will also be considered, as long as
// the square is in the attacker's mobility area.
unsafeChecks &= mobilityArea[Them];

kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
+ 69 * kingAttacksCount[Them]
+ 185 * popcount(kingRing[Us] & weak)
+ 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks)
+ tropism * tropism / 4
- 873 * !pos.count<QUEEN>(Them)
- 6 * mg_value(score) / 8
+ mg_value(mobility[Them] - mobility[Us])
- 30;

// Transform the kingDanger units into a Score, and subtract it from the evaluation
if (kingDanger > 0)
score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);

// Penalty when our king is on a pawnless flank
if (!(pos.pieces(PAWN) & kingFlank))

0 comments on commit 69dc556

Please sign in to comment.
You can’t perform that action at this time.