From ba4e215493de31263b9bd352af79d00193e545bf Mon Sep 17 00:00:00 2001 From: syzygy Date: Sat, 16 Sep 2017 21:49:29 +0200 Subject: [PATCH] Let ss->ply denote the number of plies from the root to the current node 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 | 9 ++++----- src/search.cpp | 19 +++++++++---------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index b6dc31b4..42c1adff 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -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; } diff --git a/src/search.cpp b/src/search.cpp index b68c8b78..eb8aea24 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -324,7 +324,7 @@ void MainThread::search() { void Thread::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; MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); @@ -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 if (thisThread == Threads.main()) static_cast(thisThread)->check_time(); - // Used to send selDepth info to GUI - if (PvNode && thisThread->selDepth < ss->ply) - 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->contHistory = &thisThread->contHistory[NO_PIECE][0]; (ss+2)->killers[0] = (ss+2)->killers[1] = 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); -- 2.39.2