syzygy wrote:More later.
So probe_dtz_no_ep() returns the correct dtz value assuming en passant is not possible in the probed position.
First probe_ab() is invoked (which thus is expected to return the correct WDL result assuming ep is not possible).
If probe_ab() returns 0, the position is a draw and probe_dtz_ep() can (and must, because the DTZ table contains a random value) return 0 immediately.
If success == 2, we know there is a (cursed) winning capture and we can (and must) return immediately as well.
The next part checks for (cursed) winning pawn moves. If we have such a move, we also know that dtz = 1 (and the compressor made use of this fact, so the DTZ table also contains random values for such positions). Obviously winning pawn moves only need to be checked for if probe_ab() returned wdl > 0.
If the position is not a draw and there are no winning captures or pawn moves, we have run out of cheap tricks and we must probe the DTZ table. Since DTZ tables are one-sided, we may be unlucky and find out that the "wrong" side is to move. In that case probe_dtz_table() will have returned success == -1.
If success == 0 (failure) or success > 0 (success), we can return.
If success == -1, we need to do a 1-ply search.
Because of the structure of the dtz values, it is easier to distinguish between wdl > 0 and wdl < 0.
If wdl > 0, then we want to find the move with the smallest winning (i.e. positive) dtz value. We can skip the captures and pawn moves, because we already know that those are not winning. Since we only try non-zeroing moves, the dtz value for a winning move is -probe_dtz(position_after_the_move) + 1.
If wdl < 0, then we want to find the move with the highest losing (i.e. negative) dtz value. Of course, all moves will return a negative dtz value because the position is known to lose.
The dtz value for a losing non-zeroing move is -probe_dtz(position_after_the_move) - 1.
For losing zeroing moves we have to be a bit more careful. A zeroing move always has distance to zero equal to 1. So those moves count as either -1 (in case of a real loss) or as -101 (in case of a cursed loss).
If wdl == -2, the position is a real loss, so all moves are real losses, so also all zeroing moves. So no need to investigate further.
If wdl == -1, the position is a cursed loss, so each move is either a real loss or a cursed loss (and at least one move is a cursed loss). So we need to investigate a bit for zeroing moves:
Code: Select all
v = probe_ab(pos, 1, 2, success);
v = (v == 2) ? 0 : -101;
The first line looks up the wdl value for the position after the zeroing move. We already know that that position is either a cursed win (= 1) or a real win (= 2). So to a bit more efficient, we call probe_ab() with alpha = 1 and beta = 2. If it returns 2, then the position is a real win, so the zeroing move is a real loss. If it returns < 2, then the position is a cursed win, so the zeroing move is a cursed loss.
The real losing zeroing move should count as -1 and the cursed losing zeroing move as -101. I guess the reason for "0 : -101" is that in that situation, if the zeroing move we are looking leads to a real loss, we know that some other move will anyway be "better" (namely a cursed loss). So it does not matter whether we count it as 0 or -1. But less confusing would have been -1 in this place. So to recapitulate what I just wrote (with the suggested readability change):
Code: Select all
pos.do_move(move, st, pos.gives_check(move, ci));
if (st.rule50 == 0) { // is this move a zeroing move? (capture or pawn move)
if (wdl == -2) v = -1; // if the position is a real loss, the move has dtz = -1
else { // if the position is a cursed loss, we have to take a closer look
v = probe_ab(pos, 1, 2, success); // test for cursed win or win (for the position after the zeroing move)
v = (v == 2) ? -1 : -101; // if win, count move as -1; if cursed win, count as -101
}
} else {
v = -Tablebases::probe_dtz(pos, success) - 1; // for non-zeroing move
}
pos.undo_move(move);