Question to syzygy author

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

petero2
Posts: 684
Joined: Mon Apr 19, 2010 7:07 pm
Location: Sweden
Full name: Peter Osterlund

Re: Question to syzygy author

Post by petero2 »

mcostalba wrote:P.S: Regrading figuring out positions to excercize this code path, well it seems we are talking of potential stalemates when the ep move is the only possible and the code path has an effect only if the ep is losing....very difficult to figure out such positions with 5-men.
I have not followed this discussion in detail, but I think this post contains some positions of the type you are looking for.
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Question to syzygy author

Post by mcostalba »

syzygy wrote:Then we have probe_dtz()
Thanks for your detailed explanations!

In this case I went really in razor-mode here: first I have integrated the winning pawn move in search() then I fully simplified the whole probe_dtz():

https://github.com/official-stockfish/S ... .cpp#L1305

Now the next step is to verify I made it right :-)

I will need some test positions for this (thanks Peter!), and is not trivial becuase many cases are extremely rare (with 5-men) and occur only on hand-crafted positions.
syzygy
Posts: 5557
Joined: Tue Feb 28, 2012 11:56 pm

Re: Question to syzygy author

Post by syzygy »

mcostalba wrote:
syzygy wrote: One problem is that there may be two en passant captures. However, I personally don't like this modification.
Yes you are right! I have fixed the code to handle up to 2 ep moves (although it is very difficult to figure out such positions with 5-men, perhaps with 6-men).
True, there cannot be many 5-piece positions with two en passant captures that would be stalemate without them.

Probably 7 pieces are needed:
[D]7B/8/8/8/1pPp4/1K6/3N4/k7 b - c3 0 1

But still, it is better to have the logic correct.
Regrading optimization, unfortunatly this topic is 10% intuition and 90% measurement. I plan to correctly profile the code after I have simplified it and eventually I will (re)add an optimization if profile shows me it is useul. For the moment I prefer to proceed with the simplest code base (that usually it is also the fastest and best).
I guess that in principle the idea to let probe_ab() take into account en passant is not that bad and might also simplify probe_dtz().

The major inefficiency that I see is the generation of all legal moves in every call to probe_ab(), even though checking for stalemate is only necessary in the very rare case that there all captures including at least one ep capture are losing and probe_wdl_table() returns a draw score. (In 99.999% of the calls to probe_ab() en passant will not be possible, because even probe_wdl() is normally called only after a capture.)
P.S: Regrading figuring out positions to excercize this code path, well it seems we are talking of potential stalemates when the ep move is the only possible and the code path has an effect only if the ep is losing....very difficult to figure out such positions with 5-men.
It is very rare, but it happens.
syzygy
Posts: 5557
Joined: Tue Feb 28, 2012 11:56 pm

Re: Question to syzygy author

Post by syzygy »

syzygy wrote:Probably 7 pieces are needed:
[D]7B/8/8/8/1pPp4/1K6/3N4/k7 b - c3 0 1
6 pieces suffice:
[D]8/8/8/8/1pPp4/1K1Q4/8/k7 b - c3 0 1
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Question to syzygy author

Post by mcostalba »

mcostalba wrote: Now the next step is to verify I made it right :-)
Just checked against [d]k1K5/8/8/8/4pP2/4Q3/8/8 b - f3 0 1[/d]

It works! :-)

I have added some instrumenting code in a separate branch to verify that the output of WDl and DTZ probing is the same of the original reference. Indeed simple bench number is not feasible here because move generation inside TB probing code is done with a different ordering and this affects searched nodes, but the instrumented code ensures tha the return value and state of each probe call is equivalent to the reference one.

I have also added some debug UI commands 'wdl' and 'dtz' that probe respectively WDL and DTZ tables on the current position. This is useful to debug on a specific position.
syzygy
Posts: 5557
Joined: Tue Feb 28, 2012 11:56 pm

Re: Question to syzygy author

Post by syzygy »

You have probably noticed that there is a reason to do

Code: Select all

        if (wdl > 0)
            dtz = -probe_dtz(pos, result);
and add 1 later to the "best positive dtz found" instead of doing immediately

Code: Select all

        if (wdl > 0)
            dtz = -probe_dtz(pos, result) + 1;
In the latter case, a draw value would incorrecly turn into a win value.

Regarding the new ep logic, the way ep is dealt with around probe_wdl_table() seems correct.

However, there is a problem around the call to probe_dtz_table().
This function should not be called if the position without ep is a draw. But the current code will call probe_dtz_table() if the position is a loss (because of a losing ep capture) and the position without ep is a stalemate.
syzygy
Posts: 5557
Joined: Tue Feb 28, 2012 11:56 pm

Re: Question to syzygy author

Post by syzygy »

Let's have a look at this code.

Code: Select all

    // TB tables do not contain information on position with ep rights, so in
    // this case probe_wdl_table could be wrong. In particular could be higher
    // then epValue and if epValue is the only move then we are forced to play
    // the losing ep capture.
    if (epValue != WDLScoreNone && !moveCount)
        value = epValue;

    // Here alpha stores the best value out of the previous search
    if (value > alpha)
        return *result = (value == epValue && value > WDLDraw ? WIN_CAPTURE : OK), value;

    if (bestMove && alpha > WDLDraw)
        *result = (!CheckPawnMoves || pos.capture(bestMove)) ? WIN_CAPTURE : WIN_PAWN_MOVE;
    else
        *result = OK;

    return alpha;
What it should do is signal to probe_dtz() that the best move is a winning capture or a winning pawn move (possibly cursed). This will normally be the case if alpha >= value and alpha > 0.

There is now the complication that we may have had to override "value" returned by probe_wdl_table() with epValue. But in this case we also want to signal to probe_dtz() that the best move is a capture (the ep capture).

I think there is no need to distinguish between WIN_CAPTURE and WIN_PAWN_MOVE (and whatever you would like to call the situation that the best move is a losing ep capture).

So:

Code: Select all

    if &#40;value == WDLDraw && !moveCount && epValue != WDLScoreNone && epValue < WDLDraw&#41;
        return *result = BEST_1_PLY, epValue;

    if &#40;alpha >= value&#41;
        return *result = alpha > WDLDraw ? BEST_1_PLY &#58; OK, alpha;

    return *result = OK, value;
And in probe_dtz():

Code: Select all

    if (*result == BEST_1_PLY&#41;
        return -1 / -101 / 101 / 1 for wdl = -2 / -1 / 1 / 2;
(The last line is not valid C++, obviously.)

This might fix the problem I noticed above.
syzygy
Posts: 5557
Joined: Tue Feb 28, 2012 11:56 pm

Re: Question to syzygy author

Post by syzygy »

syzygy wrote:And in probe_dtz():

Code: Select all

    if (*result == BEST_1_PLY&#41;
        return -1 / -101 / 101 / 1 for wdl = -2 / -1 / 1 / 2;
(The last line is not valid C++, obviously.)

This might fix the problem I noticed above.
In view of the lines that follow:

Code: Select all

    int dtz = 1 + (*result == BEST_1_PLY ? 0 &#58; probe_dtz_table&#40;pos, wdl, result&#41;);
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Question to syzygy author

Post by mcostalba »

syzygy wrote: However, there is a problem around the call to probe_dtz_table().
Thanks! This greatly helped me.

I have rewritten that code, could you please give it a look? Thanks.
syzygy
Posts: 5557
Joined: Tue Feb 28, 2012 11:56 pm

Re: Question to syzygy author

Post by syzygy »

mcostalba wrote:
syzygy wrote: However, there is a problem around the call to probe_dtz_table().
Thanks! This greatly helped me.

I have rewritten that code, could you please give it a look? Thanks.
It seems to be correct.