Last time I was in a scenario like this, it was regarding methods to improve my TT replacement scheme, and I got a lot of helpful information on this forum that helped me make changes that resulted in improvements. This time I am looking to add king safety to my evaluation function. I have already gotten a decent strength gain from it, but I have a feeling there is a lot more to be gained while still keeping it simple.
I've looked at a few different ways of evaluating king safety, and went with the one that made the most sense to me. It involves creating bitboards for inner and outer king rings of each square, and then keeping track of how many threats by enemy pieces are made on those squares. The inner ring is the squares adjacent to the king square, and the outer ring is the squares directly outside of the inner ring. So in reality, the shapes of these really look like squares, and not rings.
For finding the attacked squares, I take all the attacked squares by enemy pieces, and treat queens as transparent for bishop attacks, and queens and rooks as transparent for rook attacks. I also leave out squares attacked by pawns. Whenever a piece is found to be attacking a square on either king ring, I increase a variable for the number of king attackers for that side, and increase the total weight for the number of attacked squares multiplied by a weight for the type of piece attacking.
these are the tunable parameters for king safety (middlegame, endgame)
Code: Select all
Score king_safety_reduction_factor = Score(6, 6);
Score inner_ring_attack[7] = { Score(0, 0), Score(0, 0), Score(3, 9), Score(7, 2), Score(6, 1), Score(5, 0), Score(0, 0), };
Score outer_ring_attack[7] = { Score(0, 0), Score(0, 0), Score(3, 1), Score(1, 3), Score(2, 0), Score(2, 1), Score(0, 0), };
Code: Select all
if (pt > PAWN && pt < KING)
{
Bitboard attacks;
//queens are transparent for bishops
if (pt == BISHOP)
attacks = Bitboards::get_attacks(pos.occ_bitboard[BOTH] & ~(pos.piece_bitboard[W_QUEEN] | pos.piece_bitboard[B_QUEEN]), sq, BISHOP);
//queens and rooks are transparent for rooks
else if (pt == ROOK)
attacks = Bitboards::get_attacks(pos.occ_bitboard[BOTH] & ~(pos.piece_bitboard[W_QUEEN] | pos.piece_bitboard[B_QUEEN] | pos.piece_bitboard[W_ROOK] | pos.piece_bitboard[B_ROOK]), sq, ROOK);
else
attacks = Bitboards::get_attacks(pos.occ_bitboard[BOTH], sq, pt);
// outer ring attacked squares bitboard
Bitboard or_att_bb = attacks & king_zones[king_sq[them]].outer_ring;
// inner ring attacked squares bitboard
Bitboard ir_att_bb = attacks & king_zones[king_sq[them]].inner_ring;
if (or_att_bb || ir_att_bb) {
++n_king_attackers[us];
king_attack_weight[us] += inner_ring_attack[pt] * popcnt(ir_att_bb) + outer_ring_attack[pt] * popcnt(or_att_bb);
}
}
we only apply it if the opponent has a queen, and there are at least 2 attackers. these are also the conditions used by blunder.
Code: Select all
if (n_king_attackers[BLACK] >= 2 && pos.piece_bitboard[B_QUEEN])
score[WHITE] -= (king_attack_weight[BLACK] * king_attack_weight[BLACK]) / (king_safety_reduction_factor + 1);
if (n_king_attackers[WHITE] >= 2 && pos.piece_bitboard[W_QUEEN])
score[BLACK] -= (king_attack_weight[WHITE] * king_attack_weight[WHITE]) / (king_safety_reduction_factor + 1);
Are there any blaringly obvious problems with my implementation of king safety? Any obvious improvements to be made? I appreciate any thoughts.