Question about king safety scaling

Discussion of chess software programming and technical issues.

Moderator: Ras

niel5946
Posts: 174
Joined: Thu Nov 26, 2020 10:06 am
Full name: Niels Abildskov

Question about king safety scaling

Post by niel5946 »

Hi everyone :)

During the last couple of days, I have been trying to re-write my king safety implementation for Loki since it isn't compatible with the new tuner I am planning to write. Therefore, I have been looking for inspiration in other engines and on CPW.
In the former, I have noticed in Stockfish, as well as Ethereal, that king safety is scored differently from other terms. They use some kind of safety-counter and then scale it in a non-linear fashion at the end, to get the cp evaluation. They do it the following ways:

Code: Select all

#Stockfish:
score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);

# Ethereal:
mg = ScoreMG(safety), eg = ScoreEG(safety);
eval += MakeScore(-mg * MAX(0, mg) / 720, -MAX(0, eg) / 20);
What is the reason for this kind of scaling? I can understand if it's because they want the engine to clearly prefer a position with safety = 100 instead of one with safety = 300, but wouldn't proper tuning and linear terms be able to take care of this?
I also don't understand the reason for the difference between scaling in the middlegame and endgame. Why is it preferable to scale the endgame safety score linearly, but not the middlegame one?


Thanks in advance :D
Author of Loki, a C++ work in progress.
Code | Releases | Progress Log |
jdart
Posts: 4410
Joined: Fri Mar 10, 2006 5:23 am
Location: http://www.arasanchess.org

Re: Question about king safety scaling

Post by jdart »

I factor several terms into the king safety eval, and then the final step is to run it through a sigmoid function (actually implemented with a non-linear lookup table). The reason is this: one or two minors or a pawn or two in the king attack zone is probably not a serious attack. So attacks with few pieces participating, or only minor pieces, get downgraded in severity. On the other hand, once you have multiple pieces, especially majors, involved, or if the king is very exposed, then that's very serious. So after some point, those attacks get a bonus. However, as more pieces participate, adding another attacking piece is not really making it that more serious: the attack score is already high and additional pieces should give a smaller boost. The sigmoid function captures all this.
niel5946
Posts: 174
Joined: Thu Nov 26, 2020 10:06 am
Full name: Niels Abildskov

Re: Question about king safety scaling

Post by niel5946 »

jdart wrote: Wed Aug 11, 2021 4:04 pm I factor several terms into the king safety eval, and then the final step is to run it through a sigmoid function (actually implemented with a non-linear lookup table). The reason is this: one or two minors or a pawn or two in the king attack zone is probably not a serious attack. So attacks with few pieces participating, or only minor pieces, get downgraded in severity. On the other hand, once you have multiple pieces, especially majors, involved, or if the king is very exposed, then that's very serious. So after some point, those attacks get a bonus. However, as more pieces participate, adding another attacking piece is not really making it that more serious: the attack score is already high and additional pieces should give a smaller boost. The sigmoid function captures all this.
That seems like a very good idea! How do you determine the sigmoid's parameters though? Do you just use a normal sigmoid with range [0; 1] to scale down the safety value (which should then be really high to start with, I suppose?) or do you use a bigger range to scale up said value?
And what do you do in the endgame? I have been thinking about the reason for scaling endgame safety linearly throughout the day, and I would think it's because exponential scaling would risk discouraging the king from advancing in the endgame. What do you think about this?

I tried implementing the king attack calculation from Toga/CPW, but it seems to lose elo... I do the attack data collecting when I calculate mobility and then I use the following function for king safety:

Code: Select all

	/// <summary>
	/// Evaluation of king attacks.
	/// </summary>
	template<EvalType T> template<SIDE S>
	void Evaluate<T>::king_safety() {
		// Constants
		constexpr SIDE Them = (S == WHITE) ? BLACK : WHITE;

		Score safety(0, 0), eval(0, 0);

		// Step 1. Only evaluate king safety when there are more than two attackers
		if (Data.king_attackers[S] > 2 || (Data.king_attackers[S] > 1 && pos->pieceBBS[QUEEN][Them] != 0)) {

			safety.mg += (Data.king_attack_value[S] * weighted_attacks[std::clamp(Data.king_attackers[S], 0, 6)].mg) / 100;
			safety.eg += (Data.king_attack_value[S] * weighted_attacks[std::clamp(Data.king_attackers[S], 0, 6)].eg) / 100;
		}

		// Step 2. Now convert the safety score to CP.
		// Note: We do this since very few attackers isn't really a problem, whereas it rises greatly even if one attacker is added.
		eval = Score(-1 * safety.mg * std::max(0, safety.mg) / 256, -1 * std::max(0, safety.eg) / 64);

		mg_score += (S == WHITE) ? eval.mg : -eval.mg;
		eg_score += (S == WHITE) ? eval.eg : -eval.eg;
	}
Author of Loki, a C++ work in progress.
Code | Releases | Progress Log |
No4b
Posts: 105
Joined: Thu Jun 18, 2020 3:21 pm
Location: Moscow
Full name: Alexander Litov

Re: Question about king safety scaling

Post by No4b »

niel5946 wrote: Wed Aug 11, 2021 10:44 pm
jdart wrote: Wed Aug 11, 2021 4:04 pm I factor several terms into the king safety eval, and then the final step is to run it through a sigmoid function (actually implemented with a non-linear lookup table). The reason is this: one or two minors or a pawn or two in the king attack zone is probably not a serious attack. So attacks with few pieces participating, or only minor pieces, get downgraded in severity. On the other hand, once you have multiple pieces, especially majors, involved, or if the king is very exposed, then that's very serious. So after some point, those attacks get a bonus. However, as more pieces participate, adding another attacking piece is not really making it that more serious: the attack score is already high and additional pieces should give a smaller boost. The sigmoid function captures all this.
That seems like a very good idea! How do you determine the sigmoid's parameters though? Do you just use a normal sigmoid with range [0; 1] to scale down the safety value (which should then be really high to start with, I suppose?) or do you use a bigger range to scale up said value?
And what do you do in the endgame? I have been thinking about the reason for scaling endgame safety linearly throughout the day, and I would think it's because exponential scaling would risk discouraging the king from advancing in the endgame. What do you think about this?

I tried implementing the king attack calculation from Toga/CPW, but it seems to lose elo... I do the attack data collecting when I calculate mobility and then I use the following function for king safety:
Back in the day I experimented a lot with KingSafety for Drofa (and for Weiss later).
Personally I found Toga Safety implementation the most simple and logical.
Lookup table that correct AttackPower based on # of king attackers that presented in CPW is pretty good (although not perfect, can be further tuned later -> hunch and test, texel, optuna tuner etc....) and up to +40-50 elo can be easily achieved using this scheme. Various experiments showed to me that for Drofa best scheme of safety is

Code: Select all

score += gS(std::max(0, attackScore), 0);
- ie. all score goes in OPENING phase of the tapered evaluation.

It should be also noted that in my experiments safety was highly dependent on your implementation of the MOBILITY. In my case the best variant was were Rooks and Bishops were able to scan through own Queens/Rooks and Queens, respectively, while Queen wasnt able to scan through own pieces, while without scanning at all my attempts were usually close to +0 elo
jdart
Posts: 4410
Joined: Fri Mar 10, 2006 5:23 am
Location: http://www.arasanchess.org

Re: Question about king safety scaling

Post by jdart »

niel5946 wrote: Wed Aug 11, 2021 10:44 pm That seems like a very good idea! How do you determine the sigmoid's parameters though?
The sigmoid parameters are auto-tuned (Texel tuning). A little tricky because of course the gradient is nonlinear.

As for the details, source code for the eval is here: https://github.com/jdart1/arasan-chess/ ... coring.cpp
User avatar
Rebel
Posts: 7402
Joined: Thu Aug 18, 2011 12:04 pm
Full name: Ed Schröder

Re: Question about king safety scaling

Post by Rebel »

You could try the old REBEL approach as a base.

http://rebel13.nl/inside_rebel.pdf#KING_SAFETY
90% of coding is debugging, the other 10% is writing bugs.
niel5946
Posts: 174
Joined: Thu Nov 26, 2020 10:06 am
Full name: Niels Abildskov

Re: Question about king safety scaling

Post by niel5946 »

Thank you all for the answers :D
Right after I posted about my king attack implementation yesterday, I noticed that the Data.king_attack_value counters only counted the attacking piece instead of the number of squares it attacked... When I fixed this, I got the following results against the old king safety implementation:

Code: Select all

Score of Lokidev vs master: 431 - 348 - 447  [0.534] 1226
...      Lokidev playing White: 232 - 153 - 228  [0.564] 613
...      Lokidev playing Black: 199 - 195 - 219  [0.503] 613
...      White vs Black: 427 - 352 - 447  [0.531] 1226
Elo difference: 23.6 +/- 15.5, LOS: 99.9 %, DrawRatio: 36.5 %
SPRT: llr 2.97 (100.9%), lbound -2.94, ubound 2.94 - H1 was accepted
I think that this is enough to move onto other parts of king safety (weak squares, defenders etc..).
I already have code for scoring the king's pawn shield, but this isn't included in safety calculation (it is just a normal, linear term) and I think it should be. My reasoning is that a bad pawn shield should receive an extra penalty if the king is also being attacked at the same time.
Additionally, I will try implementing some code for storming enemy pawns. I tried to do this yesterday, but it didn't give good results even though I scored them exactly the same as the pawn shield (with other values of course..).
No4b wrote: Thu Aug 12, 2021 12:52 am Various experiments showed to me that for Drofa best scheme of safety is

Code: Select all

score += gS(std::max(0, attackScore), 0);
- ie. all score goes in OPENING phase of the tapered evaluation.
I just started a test where I don't include endgame king safety in the evaluation :) I suppose you do this in order to not risk discouraging the king from advancing?
I still use the middlegame scaling:

Code: Select all

mg_score += -1 * safety * std::max(safety, 0) / 256;
jdart wrote: Thu Aug 12, 2021 4:08 am The sigmoid parameters are auto-tuned (Texel tuning). A little tricky because of course the gradient is nonlinear.

As for the details, source code for the eval is here: https://github.com/jdart1/arasan-chess/ ... coring.cpp
I just looked through the code in your link. The idea of using a sigmoid function for attack scoring reminded me of the safety table/attack units method proposed on the CPW. The tables there also rise in a logistic fashion it seems.
I tried using an attack table/sigmoidal approach before, but it didn't really work, and I think there are two reasons for that: 1) I only scored the attacks, which means that I rarely got big safety scores even though a large attack was happening, and 2) Due to my current tuner being an SPSA one, it was hard to tune the table since the scaling made perturbations of the values less significant (smaller gradients).
I still have the second problem, which is why I am trying to implement a new tuner.
Rebel wrote: Thu Aug 12, 2021 6:10 am You could try the old REBEL approach as a base.

http://rebel13.nl/inside_rebel.pdf#KING_SAFETY
Thanks for the link. I tried this approach, with a safety table, but saw only small gains. I think, however, that that's because I didn't implement it properly.
If I get more problems with the Toga approach, I will take a shot at this one again :)
Author of Loki, a C++ work in progress.
Code | Releases | Progress Log |
niel5946
Posts: 174
Joined: Thu Nov 26, 2020 10:06 am
Full name: Niels Abildskov

Re: Question about king safety scaling

Post by niel5946 »

Another problem I have noticed is that while my new attack scoring gains ~24 elo, my time-to-depth is significantly higher... Has anyone had this problem before?
I think it might have something to do with move ordering being worse, but that doesn't really make sense if the evaluation is better.. :?

PS. My nps is pretty much the same between the new king safety implementation and the master branch.
Author of Loki, a C++ work in progress.
Code | Releases | Progress Log |
amanjpro
Posts: 883
Joined: Sat Mar 13, 2021 1:47 am
Full name: Amanj Sherwany

Re: Question about king safety scaling

Post by amanjpro »

niel5946 wrote: Thu Aug 12, 2021 2:58 pm Another problem I have noticed is that while my new attack scoring gains ~24 elo, my time-to-depth is significantly higher... Has anyone had this problem before?
I think it might have something to do with move ordering being worse, but that doesn't really make sense if the evaluation is better.. :?

PS. My nps is pretty much the same between the new king safety implementation and the master branch.
I wouldn't worry too much about time to depth, obviously you have only tested it on a finite set of positions...
I can make my LMR too aggressive, that time to depth sky rockets, but is that good?
What probably happens is that, previously you were pruning lines that deemed boring, now you prune more carefully, and lines where the king is in danger is searched deeper
niel5946
Posts: 174
Joined: Thu Nov 26, 2020 10:06 am
Full name: Niels Abildskov

Re: Question about king safety scaling

Post by niel5946 »

amanjpro wrote: Thu Aug 12, 2021 5:40 pm I wouldn't worry too much about time to depth, obviously you have only tested it on a finite set of positions...
I can make my LMR too aggressive, that time to depth sky rockets, but is that good?
What probably happens is that, previously you were pruning lines that deemed boring, now you prune more carefully, and lines where the king is in danger is searched deeper
That is probably right. However, I think that even without a specialized king safety term, all engines evaluate it through search. What this means is that although I should use more time searching lines where the king might be in danger, some lines would also be expected to be skipped earlier since the evaluation would cause a beta cutoff (before the eventual mate in that line is resolved fully)...
I think tuning the king safety term will help my time-to-depth a little. Otherwise, I might be able to make LMR a little more aggressive since the eval is better at differentiating between good and bad positions.
Author of Loki, a C++ work in progress.
Code | Releases | Progress Log |