From 8ab9c2511a36a929a17a689125c919c927aee786 Mon Sep 17 00:00:00 2001 From: lucasart Date: Mon, 3 Nov 2014 23:36:24 +0800 Subject: [PATCH] Cleanup MAX_PLY This area has become obscure and tricky over the course of incremental changes that did not respect the original's consistency and clarity. Now, it's not clear why we use MAX_PLY = 120, or why we use MAX_PLY+6, among other things. This patch does the following: * ID loop: depth ranges from 1 to MAX_PLY-1, and due to TT constraint (depth must fit into an int8_t), MAX_PLY should be 128. * stack[]: plies now range from 0 to MAX_PLY-1, hence stack[MAX_PLY+4], because of the extra 2+2 padding elements (for ss-2 and ss+2). Document this better, while we're at it. * Enforce 0 <= ply < MAX_PLY: - stop condition is now ss->ply >= MAX_PLY and not ss->ply > MAX_PLY. - assert(ss->ply < MAX_PLY), before using ss+1 and ss+2. - as a result, we don't need the artificial MAX_PLY+6 range. Instead we can use MAX_PLY+4 and it's clear why (for ss-2 and ss+2). * fix: extract_pv_from_tt() and insert_pv_in_tt() had no reason to use MAX_PLY_PLUS_6, because the array is indexed by plies, so the range of available plies applies (0..MAX_PLY before, and now 0..MAX_PLY-1). Tested with debug compile, using MAX_PLY=16, and running bench at depth 17, using 1 and 7 threads. No assert() fired. Feel free to submit to more severe crash-tests, if you can think of any. No functional change. --- src/search.cpp | 29 +++++++++++++++++------------ src/types.h | 5 ++--- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ea1ed57a..f6cf7090 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -240,7 +240,7 @@ namespace { void id_loop(Position& pos) { - Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2) + Stack stack[MAX_PLY+4], *ss = stack+2; // To allow referencing (ss-2) and (ss+2) int depth; Value bestValue, alpha, beta, delta; @@ -265,7 +265,7 @@ namespace { multiPV = std::max(multiPV, skill.candidates_size()); // Iterative deepening loop until requested to stop or target depth reached - while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth)) + while (++depth < MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth)) { // Age out PV variability metric BestMoveChanges *= 0.5; @@ -430,10 +430,7 @@ namespace { moveCount = quietCount = 0; bestValue = -VALUE_INFINITE; - ss->currentMove = ss->ttMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; ss->ply = (ss-1)->ply + 1; - (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO; - (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; // Used to send selDepth info to GUI if (PvNode && thisThread->maxPly < ss->ply) @@ -442,8 +439,8 @@ namespace { if (!RootNode) { // Step 2. Check for aborted search and immediate draw - if (Signals.stop || pos.is_draw() || ss->ply > MAX_PLY) - return ss->ply > MAX_PLY && !inCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; + if (Signals.stop || pos.is_draw() || ss->ply >= MAX_PLY) + return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; // Step 3. Mate distance pruning. Even if we mate at the next move our score // would be at best mate_in(ss->ply+1), but if alpha is already bigger because @@ -457,6 +454,12 @@ namespace { return alpha; } + assert(0 <= ss->ply && ss->ply < MAX_PLY); + + ss->currentMove = ss->ttMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; + (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO; + (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; + // Step 4. Transposition table lookup // We don't want the score of a partial search to overwrite a previous full search // TT value, so we use a different position key in case of an excluded move. @@ -1021,8 +1024,10 @@ moves_loop: // When in check and at SpNode search starts from here ss->ply = (ss-1)->ply + 1; // Check for an instant draw or if the maximum ply has been reached - if (pos.is_draw() || ss->ply > MAX_PLY) - return ss->ply > MAX_PLY && !InCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; + if (pos.is_draw() || ss->ply >= MAX_PLY) + return ss->ply >= MAX_PLY && !InCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; + + assert(0 <= ss->ply && ss->ply < MAX_PLY); // Decide whether or not to include checks: this fixes also the type of // TT entry depth that we are going to use. Note that in qsearch we use @@ -1352,7 +1357,7 @@ moves_loop: // When in check and at SpNode search starts from here void RootMove::extract_pv_from_tt(Position& pos) { - StateInfo state[MAX_PLY_PLUS_6], *st = state; + StateInfo state[MAX_PLY], *st = state; const TTEntry* tte; int ply = 1; // At root ply is 1... Move m = pv[0]; // ...instead pv[] array starts from 0 @@ -1388,7 +1393,7 @@ void RootMove::extract_pv_from_tt(Position& pos) { void RootMove::insert_pv_in_tt(Position& pos) { - StateInfo state[MAX_PLY_PLUS_6], *st = state; + StateInfo state[MAX_PLY], *st = state; const TTEntry* tte; int idx = 0; // Ply starts from 1, we need to start from 0 @@ -1430,7 +1435,7 @@ void Thread::idle_loop() { Threads.mutex.unlock(); - Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2) + Stack stack[MAX_PLY+4], *ss = stack+2; // To allow referencing (ss-2) and (ss+2) Position pos(*sp->pos, this); std::memcpy(ss-2, sp->ss-2, 5 * sizeof(Stack)); diff --git a/src/types.h b/src/types.h index 3ff07ee7..cbef89b7 100644 --- a/src/types.h +++ b/src/types.h @@ -100,9 +100,8 @@ const bool Is64Bit = false; typedef uint64_t Key; typedef uint64_t Bitboard; -const int MAX_MOVES = 256; -const int MAX_PLY = 120; -const int MAX_PLY_PLUS_6 = MAX_PLY + 6; +const int MAX_MOVES = 256; +const int MAX_PLY = 128; /// A move needs 16 bits to be stored /// -- 2.39.2