author syzygy Sat, 16 Sep 2017 19:49:29 +0000 (21:49 +0200) committer Marco Costalba Sun, 17 Sep 2017 08:44:10 +0000 (10:44 +0200)
This patch lets ss->ply be equal to 0 at the root of the search.

Currently, the root has ss->ply == 1, which is less intuitive:

- Setting the rootNode bool has to check (ss-1)->ply == 0.

- All mate values are off by one: the code seems to assume that mated-in-0
is -VALUE_MATE, mate-1-in-ply is VALUE_MATE-1, mated-in-2-ply is VALUE_MATE+2, etc.
But the mate_in() and mated_in() functions are called with ss->ply, which is 1 in
at the root.

- The is_draw() function currently needs to explain why it has "ply - 1 > i" instead
of simply "ply > i".

- The ss->ply >= MAX_PLY tests in search() and qsearch() already assume that
ss->ply == 0 at the root. If we start at ss->ply == 1, it would make more sense to
go up to and including ss->ply == MAX_PLY, so stop at ss->ply > MAX_PLY. See also
the asserts testing for 0 <= ss->ply && ss->ply < MAX_PLY.

The reason for ss->ply == 1 at the root is the line "ss->ply = (ss-1)->ply + 1" at
the start for search() and qsearch(). By replacing this with "(ss+1)->ply = ss->ply + 1"
we keep ss->ply == 0 at the root. Note that search() already clears killers in (ss+2),
so there is no danger in accessing ss+1.

I have NOT changed pv[MAX_PLY + 1] to pv[MAX_PLY + 2] in search() and qsearch().
It seems to me that MAX_PLY + 1 is exactly right:

- MAX_PLY entries for ss->ply running from 0 to MAX_PLY-1, and 1 entry for the
final MOVE_NONE.

I have verified that mate scores are reported correctly. (They were already reported
correctly due to the extra ply being rounded down when converting to moves.)

The value of seldepth output to the user should probably not change, so I add 1 to it.
(Humans count from 1, computers from 0.)

A small optimisation I did not include: instead of setting ss->ply in every invocation
of search() and qsearch(), it could be set once for all plies at the start of
Thread::search(). This saves a couple of instructions per node.

No functional change (unless the search searches a branch MAX_PLY deep), so bench
does not change.

 src/position.cpp patch | blob | history src/search.cpp patch | blob | history

@@ -272,7 +272,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
// 5-6. Halfmove clock and fullmove number
ss >> std::skipws >> st->rule50 >> gamePly;

-  // Convert from fullmove starting from 1 to ply starting from 0,
+  // Convert from fullmove starting from 1 to gamePly starting from 0,
// handle also common incorrect FEN with fullmove = 0.
gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);

@@ -1071,11 +1071,10 @@ bool Position::is_draw(int ply) const {
{
stp = stp->previous->previous;

-      // At root position ply is 1, so return a draw score if a position
-      // repeats once earlier but strictly after the root, or repeats twice
-      // before or at the root.
+      // Return a draw score if a position repeats once earlier but strictly
+      // after the root, or repeats twice before or at the root.
if (   stp->key == st->key
-          && ++cnt + (ply - 1 > i) == 2)
+          && ++cnt + (ply > i) == 2)
return true;
}

index b68c8b7859a1a6e5a27b761b06c2b80c31ed4f80..eb8aea24a13abc521c07016787e9ea30f3660b15 100644 (file)
@@ -324,7 +324,7 @@ void MainThread::search() {

-  Stack stack[MAX_PLY+7], *ss = stack+4; // To allow referencing (ss-4) and (ss+2)
+  Stack stack[MAX_PLY+7], *ss = stack+4; // To reference from (ss-4) to (ss+2)
Value bestValue, alpha, beta, delta;
Move easyMove = MOVE_NONE;
@@ -527,7 +527,7 @@ namespace {
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode, bool skipEarlyPruning) {

const bool PvNode = NT == PV;
-    const bool rootNode = PvNode && (ss-1)->ply == 0;
+    const bool rootNode = PvNode && ss->ply == 0;

assert(-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE);
assert(PvNode || (alpha == beta - 1));
@@ -553,15 +553,14 @@ namespace {
moveCount = quietCount = ss->moveCount = 0;
ss->statScore = 0;
bestValue = -VALUE_INFINITE;
-    ss->ply = (ss-1)->ply + 1;

// Check for the available remaining time

-    // Used to send selDepth info to GUI
-    if (PvNode && thisThread->selDepth < ss->ply)
+    // Used to send selDepth info to GUI (selDepth counts from 1, ply from 0)
+    if (PvNode && thisThread->selDepth < ss->ply + 1)
+        thisThread->selDepth = ss->ply + 1;

if (!rootNode)
{
@@ -584,6 +583,7 @@ namespace {

assert(0 <= ss->ply && ss->ply < MAX_PLY);

+    (ss+1)->ply = ss->ply + 1;
ss->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
(ss+2)->killers = (ss+2)->killers = MOVE_NONE;
@@ -649,8 +649,8 @@ namespace {

int drawScore = TB::UseRule50 ? 1 : 0;

-                value =  v < -drawScore ? -VALUE_MATE + MAX_PLY + ss->ply
-                       : v >  drawScore ?  VALUE_MATE - MAX_PLY - ss->ply
+                value =  v < -drawScore ? -VALUE_MATE + MAX_PLY + ss->ply + 1
+                       : v >  drawScore ?  VALUE_MATE - MAX_PLY - ss->ply - 1
:  VALUE_DRAW + 2 * v * drawScore;

tte->save(posKey, value_to_tt(value, ss->ply), BOUND_EXACT,
@@ -1164,7 +1164,7 @@ moves_loop: // When in check search starts from here
}

ss->currentMove = bestMove = MOVE_NONE;
-    ss->ply = (ss-1)->ply + 1;
+    (ss+1)->ply = ss->ply + 1;
moveCount = 0;

// Check for an instant draw or if the maximum ply has been reached
@@ -1179,7 +1179,6 @@ moves_loop: // When in check search starts from here
// only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS.
ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS
: DEPTH_QS_NO_CHECKS;
-
// Transposition table lookup
posKey = pos.key();
tte = TT.probe(posKey, ttHit);