seldepth: what is the correct way to calculate this?

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

seldepth: what is the correct way to calculate this?

Post by mvanthoor »

Hi :)

My engine reports "seldepth" in its info string. It calculates it like this:

Code: Select all

if refs.search_info.ply > refs.search_info.depth {
	refs.search_info.seldepth = refs.search_info.ply;
}
So, if the number of plies it reached is bigger than the requested depth (which can only be caused by in-check extension and qsearch), it updates seldepth. If the engine reports 7/9, it means it reached depth 7, but within this depth the maximum number of plies was 9.

I noticed that some other engines who don't have a lot of extensions either, report a much higher seldepth. One of them is Pulse 1.7.2, so looked into its code. If I would change the above statement to do what Pulse does, it would be:

Code: Select all

if refs.search_info.ply > refs.search_info.seldepth {
	refs.search_info.seldepth = refs.search_info.ply;
}
(Pulse uses an intermediate variable called "MaxDepth" where I use refs.search_info.seldepth.)

This does something different of course: it checks if the number of plies reached is higher than the already stored seldepth, and if so, seldepth is replaced. So this number gets higher and higher, because it is calculated over the entire tree, instead of only the requested depth. I tried it in Rustic, and where it would normally report something like 7/9, it now reports things like 7/24.

Pulse's way doesn't feel right to me.

Rustic shows the depth, and the maximum number of plies within that depth; so 7/9 actually means 2 plies extension ("selective depth").
Pulse shows the depth it reached, and, the maximum depth reached anywhere in the tree during its search.

And lastly... I have also seen a few posts online that state that the plies caused by extension should ONLY be counted in seldepth if the moves found there end up in the PV. But that also doesn't feel right to me; the engine DID extend, and it DID search selectively deeper, even though the moves found were not good enough, so if the work is done, it should be reported.

I know this is completely cosmetic; changing or not changing this has no bearing on what and how the engine plays. Still I'd like to know your thoughts... what is the correct, or generally accepted interpretation of seldepth?
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
brianr
Posts: 536
Joined: Thu Mar 09, 2006 3:01 pm

Re: seldepth: what is the correct way to calculate this?

Post by brianr »

I think selective or seldepth is entirely up to the engine author.
Even the notion of plain old regular depth depends on the engine.
There are differences between various A/B engines, and it the case of Lc0 there is no depth (it is an estimate).

For seldepth in Tinker I happen to use the deepest ply reached with extensions for the entire search, not just the current line.
I also provide a third depth which is the deepest depth reached in the q-search.

The first and only somewhat important number is the regular "depth", which for Tinker is the full iteration depth (using IID) that was reached. Again, engines like Leela don't use IID so it is meaningless.

So, Tinker reports depth as (x/y/z) for full iteration depth reached x, depth with extensions y, and the deepest q-search ply z.
The numbers are only useful to me.
Sesse
Posts: 300
Joined: Mon Apr 30, 2018 11:51 pm

Re: seldepth: what is the correct way to calculate this?

Post by Sesse »

I've seen Stockfish report lower seldepth than depth! (I guess all moves were reduced pretty hard at some point.) AFAIK it uses the same notion of “seldepth is the highest depth we've ever reached”.
unserializable
Posts: 64
Joined: Sat Oct 24, 2020 6:39 pm
Full name: Taimo Peelo

Re: seldepth: what is the correct way to calculate this?

Post by unserializable »

Sesse wrote: Mon Feb 01, 2021 11:40 am I've seen Stockfish report lower seldepth than depth! (I guess all moves were reduced pretty hard at some point.) AFAIK it uses the same notion of “seldepth is the highest depth we've ever reached”.
Stockfish 9 here does not seem to report ever reached selective depth but extension depth for current depth iteration -- below on depth 29 from starting position it reports seldepth 39 and on depth 30 it reports (lower) seldepth 37.

Code: Select all

info depth 29 seldepth 39 multipv 1 score cp 45 nodes 107437630 nps 1171825 hashfull 999 tbhits 0 time 91684 pv d2d4 g8f6 c2c4 e7e6 g1f3 d7d5 b1c3 f8b4 c1g5 b8d7 e2e3 h7h6 g5f6 b4c3 b2c3 d7f6 f1d3 e8g8 c4d5 e6d5 e1g1 b7b6 a1b1 c7c5 h2h3 c5c4 d3c2 c8b7 f3e5 f6d7 a2a3 d7e5 d4e5
info depth 30 currmove d2d4 currmovenumber 1
...
info depth 30 seldepth 37 multipv 1 score cp 37 upperbound nodes 116343454 nps 1172663 hashfull 999 tbhits 0 time 99213 pv d2d4 g8f6
Is it not more informative, at least for engine developers and testers, to report selective depth as highest depth reached in given depth iteration, could also allow to pinpoint some edge-case happenings better?
Monchester 1.0, chess engine playing at scholastic level: https://github.com/unserializable/monchester ("Daddy, it is gonna take your horsie!")
Tickle Monchester at: https://lichess.org/@/monchester
User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: seldepth: what is the correct way to calculate this?

Post by mvanthoor »

Up until now, I've encountered four engines (including Stockfish) that basically do:

Code: Select all

if current_ply > seldepth  {
	seldepth = current_ply
}
That means they just record the maximum ply reached, wherever in the tree that may be. I've also seen other engines (Fritz, etc) show very large seldepths, which can only be maximum depth reached.

One engine (and Rustic also, before today) did:

Code: Select all

if  current_ply > requested_depth {
	seldepth = current_ply
}
This means that the highest ply reached in the currently requested depth (as in: go depth 8, for example) will be recorded.

The second version feels more logical to me, because it shows the ply the engine reached when actually extending the requested depth, but the first, where the engine reports the maximum depth it reached, somewhere, seems to be the default. (Even though the evidence is somewhat anecdotal, because I can't test every engine and look at all source code; and some engines don't even HAVE seldepth in their reports.)
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
unserializable
Posts: 64
Joined: Sat Oct 24, 2020 6:39 pm
Full name: Taimo Peelo

Re: seldepth: what is the correct way to calculate this?

Post by unserializable »

mvanthoor wrote: Mon Feb 01, 2021 10:24 pm Up until now, I've encountered four engines (including Stockfish) that basically do:
... but the first, where the engine reports the maximum depth it reached, somewhere, seems to be the default. (Even though the evidence is somewhat anecdotal ...
Empirically, that was not the case for SF9 I was using and SF current master resets selDepth at each iterative deepening step, so their reporting should be like (previously) in Rustic.

Depth 70 startpos log of SF 180113: http://www.fastgm.de/Initial_position1.html
Monchester 1.0, chess engine playing at scholastic level: https://github.com/unserializable/monchester ("Daddy, it is gonna take your horsie!")
Tickle Monchester at: https://lichess.org/@/monchester
User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: seldepth: what is the correct way to calculate this?

Post by mvanthoor »

unserializable wrote: Tue Feb 02, 2021 8:52 am
mvanthoor wrote: Mon Feb 01, 2021 10:24 pm Up until now, I've encountered four engines (including Stockfish) that basically do:
... but the first, where the engine reports the maximum depth it reached, somewhere, seems to be the default. (Even though the evidence is somewhat anecdotal ...
Empirically, that was not the case for SF9 I was using and SF current master resets selDepth at each iterative deepening step, so their reporting should be like (previously) in Rustic.

Depth 70 startpos log of SF 180113: http://www.fastgm.de/Initial_position1.html
In my engine it doesn't actually matter if I reset seldepth for each iteration or not; one with, and one without reset:

Code: Select all

info score cp 70 depth 1 seldepth 1 time 0 nodes 7 nps 0 pv e2e4
info score cp 0 depth 2 seldepth 4 time 0 nodes 114 nps 0 pv d2d4 d7d5
info score cp 60 depth 3 seldepth 8 time 0 nodes 1037 nps 0 pv d2d4 d7d5 e2e3
info score cp 0 depth 4 seldepth 11 time 4 nodes 16662 nps 4165500 pv d2d4 d7d5 c1g5 c8g4
info score cp 20 depth 5 seldepth 15 time 53 nodes 180683 nps 3409113 pv d2d4 d7d5 c1f4 c8g4 f4e5
info score cp 5 depth 6 seldepth 18 time 394 nodes 1548514 nps 3930239 pv d2d4 d7d5 e2e3 c8d7 c2c4 e7e6 c4d5 e6d5
info score cp 15 depth 7 seldepth 24 time 3292 nodes 12511658 nps 3800625 pv c2c4 b8c6 d2d3 e7e5 e2e4 f8b4 c1d2 d7d6 d2b4 c6b4
info score cp 5 depth 8 seldepth 26 time 27408 nodes 112472885 nps 4103652 pv e2e4 d7d5 e4e5 c8d7 d2d4 e7e6 c2c4 f8e7 c4d5 e6d5

info score cp 70 depth 1 seldepth 1 time 0 nodes 7 nps 0 pv e2e4
info score cp 0 depth 2 seldepth 4 time 0 nodes 114 nps 0 pv d2d4 d7d5
info score cp 60 depth 3 seldepth 8 time 0 nodes 1037 nps 0 pv d2d4 d7d5 e2e3
info score cp 0 depth 4 seldepth 11 time 4 nodes 16662 nps 4165500 pv d2d4 d7d5 c1g5 c8g4
info score cp 20 depth 5 seldepth 15 time 54 nodes 180683 nps 3345981 pv d2d4 d7d5 c1f4 c8g4 f4e5
info score cp 5 depth 6 seldepth 18 time 397 nodes 1548514 nps 3900539 pv d2d4 d7d5 e2e3 c8d7 c2c4 e7e6 c4d5 e6d5
info score cp 15 depth 7 seldepth 24 time 3324 nodes 12511658 nps 3764037 pv c2c4 b8c6 d2d3 e7e5 e2e4 f8b4 c1d2 d7d6 d2b4 c6b4
info score cp 5 depth 8 seldepth 26 time 27291 nodes 112472885 nps 4121245 pv e2e4 d7d5 e4e5 c8d7 d2d4 e7e6 c2c4 f8e7 c4d5 e6d5
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
unserializable
Posts: 64
Joined: Sat Oct 24, 2020 6:39 pm
Full name: Taimo Peelo

Re: seldepth: what is the correct way to calculate this?

Post by unserializable »

mvanthoor wrote: Tue Feb 02, 2021 10:43 am In my engine it doesn't actually matter if I reset seldepth for each iteration or not; one with, and one without reset:
...
In the samples indeed all that happens is seldepth increasing monotonously, which seems logical from the way you described the change but kind of weird when it would be reset at iterations?

Rustic 'qsearch-fix' compilation from some days ago, gives:

Code: Select all

info score cp 70 depth 1 time 0 nodes 7 nps 0 pv e2e4
info score cp 0 depth 2 seldepth 4 time 0 nodes 114 nps 0 pv d2d4 d7d5
info score cp 60 depth 3 seldepth 4 time 0 nodes 1037 nps 0 pv d2d4 d7d5 e2e3
info score cp 0 depth 4 seldepth 6 time 9 nodes 16662 nps 1851333 pv d2d4 d7d5 c1g5 c8g4
info score cp 20 depth 5 seldepth 6 time 64 nodes 180683 nps 2823172 pv d2d4 d7d5 c1f4 c8g4 f4e5
info score cp 5 depth 6 seldepth 7 time 463 nodes 1548514 nps 3344523 pv d2d4 d7d5 e2e3 c8d7 c2c4 e7e6 c4d5 e6d5
info score cp 15 depth 7 seldepth 10 time 3808 nodes 12511658 nps 3285624 pv c2c4 b8c6 d2d3 e7e5 e2e4 f8b4 c1d2 d7d6 d2b4 c6b4
info score cp 5 depth 8 seldepth 9 time 31864 nodes 112472885 nps 3529779 pv e2e4 d7d5 e4e5 c8d7 d2d4 e7e6 c2c4 f8e7 c4d5 e6d5
Monchester 1.0, chess engine playing at scholastic level: https://github.com/unserializable/monchester ("Daddy, it is gonna take your horsie!")
Tickle Monchester at: https://lichess.org/@/monchester
User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: seldepth: what is the correct way to calculate this?

Post by mvanthoor »

unserializable wrote: Tue Feb 02, 2021 3:15 pm In the samples indeed all that happens is seldepth increasing monotonously, which seems logical from the way you described the change but kind of weird when it would be reset at iterations?
I wrote some debugging code, and my previous implementation was wrong. There, seldepth contained the last instance where seldepth was larger than the requested search depth, wherever in the tree that might have been.

I've written some debug code to see what maximum ply the engine actually reaches. I set up this position:

[d]4k3/2qp3P/2P2b2/4n3/8/8/1B2Q3/3K4 w - - 0 1

Then I ran a depth 1 search. (With print-statements to see what it does. Warning: long)

Code: Select all

go depth 1
A/B - depth: 1 ply: 1 move_nr: 1 move: Bb2e5
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 2
QS - depth: 1 ply: 2
A/B - depth: 1 ply: 1 move_nr: 2 move: Qe2e5
Check Extend: depth +1
A/B - depth: 1 ply: 2 move_nr: 1 move: Bf6e5
QS - depth: 1 ply: 3
QS - depth: 1 ply: 3
A/B - depth: 1 ply: 1 move_nr: 3 move: c6d7
Check Extend: depth +1
A/B - depth: 1 ply: 2 move_nr: 2 move: Qc7d7
Check Extend: depth +1
A/B - depth: 1 ply: 3 move_nr: 3 move: Kd1c2
A/B - depth: 1 ply: 3 move_nr: 9 move: Bb2d4
A/B - depth: 1 ply: 3 move_nr: 10 move: Kd1c1
A/B - depth: 1 ply: 3 move_nr: 14 move: Qe2d2
A/B - depth: 1 ply: 3 move_nr: 18 move: Qe2d3
A/B - depth: 1 ply: 3 move_nr: 25 move: Kd1e1
A/B - depth: 1 ply: 1 move_nr: 4 move: Kd1d2
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
A/B - depth: 1 ply: 1 move_nr: 5 move: Bb2a1
A/B - depth: 1 ply: 1 move_nr: 6 move: Bb2c1
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
A/B - depth: 1 ply: 1 move_nr: 7 move: Bb2a3
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
A/B - depth: 1 ply: 1 move_nr: 8 move: Bb2c3
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
A/B - depth: 1 ply: 1 move_nr: 9 move: Bb2d4
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
A/B - depth: 1 ply: 1 move_nr: 10 move: Kd1c1
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
A/B - depth: 1 ply: 1 move_nr: 11 move: Qe2e1
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
A/B - depth: 1 ply: 1 move_nr: 12 move: Qe2f1
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
A/B - depth: 1 ply: 1 move_nr: 13 move: Qe2c2
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
A/B - depth: 1 ply: 1 move_nr: 14 move: Qe2d2
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
A/B - depth: 1 ply: 1 move_nr: 15 move: Qe2f2
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
A/B - depth: 1 ply: 1 move_nr: 16 move: Qe2g2
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
A/B - depth: 1 ply: 1 move_nr: 17 move: Qe2h2
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
A/B - depth: 1 ply: 1 move_nr: 18 move: Qe2d3
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 3
A/B - depth: 1 ply: 1 move_nr: 19 move: Qe2e3
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
A/B - depth: 1 ply: 1 move_nr: 20 move: Qe2f3
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 3
A/B - depth: 1 ply: 1 move_nr: 21 move: Qe2c4
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 3
A/B - depth: 1 ply: 1 move_nr: 22 move: Qe2e4
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
A/B - depth: 1 ply: 1 move_nr: 23 move: Qe2g4
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 3
A/B - depth: 1 ply: 1 move_nr: 24 move: Qe2b5
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
A/B - depth: 1 ply: 1 move_nr: 25 move: Kd1e1
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
A/B - depth: 1 ply: 1 move_nr: 26 move: Qe2h5
Check Extend: depth +1
A/B - depth: 1 ply: 2 move_nr: 4 move: Ke8f8
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 7
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
A/B - depth: 1 ply: 2 move_nr: 9 move: Ke8e7
A/B - depth: 1 ply: 2 move_nr: 10 move: Ne5g6
A/B - depth: 1 ply: 2 move_nr: 11 move: Ne5f7
A/B - depth: 1 ply: 2 move_nr: 28 move: Ke8d8
A/B - depth: 1 ply: 1 move_nr: 27 move: Qe2a6
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
A/B - depth: 1 ply: 1 move_nr: 28 move: Kd1c2
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
A/B - depth: 1 ply: 1 move_nr: 29 move: h7h8q
Check Extend: depth +1
A/B - depth: 1 ply: 2 move_nr: 1 move: Bf6h8
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 7
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
A/B - depth: 1 ply: 1 move_nr: 30 move: h7h8r
Check Extend: depth +1
A/B - depth: 1 ply: 2 move_nr: 1 move: Bf6h8
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 7
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
A/B - depth: 1 ply: 1 move_nr: 31 move: h7h8b
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 7
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
A/B - depth: 1 ply: 1 move_nr: 32 move: h7h8n
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 7
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
info score cp -110 depth 1 seldepth 7 time 1 nodes 201 nps 201000 pv e2h5 e8f8
bestmove e2h5
quit
As you can see, it indeed stays at depth 1, but it does reach ply 7 as a maximum. Here it's shown that the check extension works, and that qsearch extends off of that:

Code: Select all

A/B - depth: 1 ply: 1 move_nr: 26 move: Qe2h5
Check Extend: depth +1
A/B - depth: 1 ply: 2 move_nr: 4 move: Ke8f8
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 7
...
The black king is checked, and moves out of check while still in alpha/beta, before qsearch is entered.

In the KiwiPete position, QS is massive.

Code: Select all

go depth 1
....
A/B - depth: 1 ply: 1 move_nr: 39 move: a2a3
QS - depth: 1 ply: 2
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 7
QS - depth: 1 ply: 7
QS - depth: 1 ply: 7
QS - depth: 1 ply: 7
QS - depth: 1 ply: 7
QS - depth: 1 ply: 7
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 7
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 7
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 7
QS - depth: 1 ply: 8
QS - depth: 1 ply: 9
QS - depth: 1 ply: 10
QS - depth: 1 ply: 11
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 10
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 7
QS - depth: 1 ply: 8
QS - depth: 1 ply: 9
QS - depth: 1 ply: 10
QS - depth: 1 ply: 11
QS - depth: 1 ply: 12
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 9
QS - depth: 1 ply: 10
QS - depth: 1 ply: 11
QS - depth: 1 ply: 12
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 12
QS - depth: 1 ply: 13
QS - depth: 1 ply: 14
QS - depth: 1 ply: 14
QS - depth: 1 ply: 14
QS - depth: 1 ply: 12
QS - depth: 1 ply: 13
QS - depth: 1 ply: 14
QS - depth: 1 ply: 14
QS - depth: 1 ply: 14
QS - depth: 1 ply: 14
QS - depth: 1 ply: 14
QS - depth: 1 ply: 12
QS - depth: 1 ply: 13
QS - depth: 1 ply: 14
QS - depth: 1 ply: 15
QS - depth: 1 ply: 16
QS - depth: 1 ply: 15
QS - depth: 1 ply: 13
QS - depth: 1 ply: 14
QS - depth: 1 ply: 15
QS - depth: 1 ply: 16
QS - depth: 1 ply: 16
QS - depth: 1 ply: 14
QS - depth: 1 ply: 11
QS - depth: 1 ply: 9
QS - depth: 1 ply: 10
QS - depth: 1 ply: 9
QS - depth: 1 ply: 10
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 10
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 9
QS - depth: 1 ply: 10
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 8
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 7
QS - depth: 1 ply: 7
QS - depth: 1 ply: 7
QS - depth: 1 ply: 7
QS - depth: 1 ply: 8
QS - depth: 1 ply: 9
QS - depth: 1 ply: 10
QS - depth: 1 ply: 10
QS - depth: 1 ply: 10
QS - depth: 1 ply: 10
QS - depth: 1 ply: 10
QS - depth: 1 ply: 8
QS - depth: 1 ply: 9
QS - depth: 1 ply: 10
QS - depth: 1 ply: 11
QS - depth: 1 ply: 12
QS - depth: 1 ply: 12
QS - depth: 1 ply: 12
QS - depth: 1 ply: 10
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 10
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 9
QS - depth: 1 ply: 10
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 11
QS - depth: 1 ply: 7
QS - depth: 1 ply: 8
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 9
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 6
QS - depth: 1 ply: 7
QS - depth: 1 ply: 7
QS - depth: 1 ply: 7
QS - depth: 1 ply: 7
QS - depth: 1 ply: 7
QS - depth: 1 ply: 7
QS - depth: 1 ply: 7
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
QS - depth: 1 ply: 4
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 5
QS - depth: 1 ply: 3
QS - depth: 1 ply: 3
QS - depth: 1 ply: 3
QS - depth: 1 ply: 3
QS - depth: 1 ply: 3
QS - depth: 1 ply: 3
QS - depth: 1 ply: 3
....
info score cp 25 depth 1 seldepth 16 time 13 nodes 1598 nps 122923 pv e2a6 b4c3 b2c3 e6d5
bestmove e2a6
quit
(Somewhere in that output was a QSearch of up to 16 ply)

So Rustic as you tested it, showed an almost random seldepth (from wherever in the tree it last encountered ply > requested depth), and the current version in the master-branch shows the max depth the engine reached at a certain ply, because I have no reductions at this point. In the KiwiPete position, depth 16 seems to be reached before the position is actually quiet.

Obviously, in this case, seldepth will become higher and higher, or at least stay the same:

depth 1 seldepth 12 (reached 12)
depth 2 seldepth 12 (reached 7)
depth 2 seldepth 15 (reached 15)

I also think I know why Stockfish / most engines need to reset seldepth and ply, and I don't (if I don't want to). They probably use global variables, and they keep their information between searches. In my engine, search_info is destroyed at the end of the search (courtesy of Rust), so I don't need to reset it. Between iterations I also don't need to reset, because:
1
reset
1, 2
reset
1, 2, 3
It doesn't matter; if I reset after depth 1, the exact same information will be in the variables after 1 is finshed in the next iteration.

In the Kiwipete position with the current version of Rustic in master, this happens:

Code: Select all

go
info score cp 25 depth 1 seldepth 16 time 0 nodes 1598 nps 0 pv e2a6 b4c3 b2c3 e6d5
info score cp 25 depth 2 seldepth 16 time 0 nodes 3196 nps 0 pv e2a6 b4c3 b2c3 e6d5
info score cp 20 depth 3 seldepth 20 time 2 nodes 8556 nps 4278000 pv e2a6 e6d5 a6b7 b4c3 b7a8 c3d2
info score cp 20 depth 4 seldepth 20 time 6 nodes 21644 nps 3607333 pv e2a6 e6d5 a6b7 b4c3 b7a8 c3d2
info score cp 5 depth 5 seldepth 22 time 27 nodes 80147 nps 2968407 pv e2a6 b4c3 d2c3 e6d5 e4d5 f6d5
info score cp 15 depth 6 seldepth 22 time 131 nodes 313607 nps 2393947 pv e2a6 e6d5 c3d5 b6d5 a6b7 a8d8 b7d5 f6d5
info score cp 5 depth 7 seldepth 24 time 638 nodes 1415412 nps 2218514 pv e2a6 e6d5 c3d5 f6d5 e5c4 f7f5 c4b6 a7b6
info score cp -15 depth 8 seldepth 26 time 3695 nodes 7683695 nps 2079484 pv e2a6 e6d5 c3d5 f6d5 e5d3 f7f5 g2h3 f5e4
info score cp -40 depth 9 seldepth 27 time 21236 nodes 42283163 nps 1991108 pv d5e6 e7e6 e2a6 e6e5 g2h3 b4c3 d2c3 e5e6 c3f6 g7f6
And in the end, it does feel logical now that I have seen the debug output. If depth 1 already causes a massive QSearch of 15 ply (for a seldepth/maxdepth of 16), then it is completely believable, that somewhere int he tree at depth 3, a QSearch of 17 takes place, for a maxdepth/seldepth of 20... and so on.
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: seldepth: what is the correct way to calculate this?

Post by mvanthoor »

And, it would of course be possible to NOT treat qsearch as an extension through the removal of ply recording. In the current engine, seldepth would always be the same as depth, or one higher because of the check extension.
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL