Fix reported DTZ for mate-in-1-ply positions
authorsyzygy1 <3028851+syzygy1@users.noreply.github.com>
Sat, 14 Apr 2018 06:36:29 +0000 (08:36 +0200)
committerStéphane Nicolet <cassio@free.fr>
Sat, 14 Apr 2018 06:36:41 +0000 (08:36 +0200)
This corrects a bug in Tablebases::probe_dtz() which sometimes causes
a higher DTZ value to be returned for the position one ply before mate
than for the position two plies before mate.

The problem was reported by Kolja Kühn here:
http://talkchess.com/forum/viewtopic.php?p=757497#757497

It is explained here:
http://talkchess.com/forum/viewtopic.php?p=757506#757506

I have also adjusted some comments to make clear that probe_dtz()
returns -1 for a mate position.

Closes https://github.com/official-stockfish/Stockfish/pull/1546

No functional change

src/syzygy/tbprobe.cpp

index cb9dcdd..cc69b64 100644 (file)
@@ -1354,6 +1354,7 @@ WDLScore Tablebases::probe_wdl(Position& pos, ProbeState* result) {
 // The return value is from the point of view of the side to move:
 //         n < -100 : loss, but draw under 50-move rule
 // -100 <= n < -1   : loss in n ply (assuming 50-move counter == 0)
+//        -1        : loss, the side to move is mated
 //         0        : draw
 //     1 < n <= 100 : win in n ply (assuming 50-move counter == 0)
 //   100 < n        : win, but draw under 50-move rule
@@ -1413,10 +1414,9 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) {
         dtz = zeroing ? -dtz_before_zeroing(search(pos, result))
                       : -probe_dtz(pos, result);
 
-        pos.undo_move(move);
-
-        if (*result == FAIL)
-            return 0;
+        // If the move mates, force minDTZ to 1
+        if (dtz == 1 && pos.checkers() && MoveList<LEGAL>(pos).size() == 0)
+            minDTZ = 1;
 
         // Convert result from 1-ply search. Zeroing moves are already accounted
         // by dtz_before_zeroing() that returns the DTZ of the previous move.
@@ -1426,11 +1426,14 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) {
         // Skip the draws and if we are winning only pick positive dtz
         if (dtz < minDTZ && sign_of(dtz) == sign_of(wdl))
             minDTZ = dtz;
+
+        pos.undo_move(move);
+
+        if (*result == FAIL)
+            return 0;
     }
 
-    // Special handle a mate position, when there are no legal moves, in this
-    // case return value is somewhat arbitrary, so stick to the original TB code
-    // that returns -1 in this case.
+    // When there are no legal moves, the position is mate: we return -1
     return minDTZ == 0xFFFF ? -1 : minDTZ;
 }