Move best = MOVE_NONE;
};
- // EasyMoveManager structure is used to detect an 'easy move'. When the PV is stable
- // across multiple search iterations, we can quickly return the best move.
- struct EasyMoveManager {
-
- void clear() {
- stableCnt = 0;
- expectedPosKey = 0;
- pv[0] = pv[1] = pv[2] = MOVE_NONE;
- }
-
- Move get(Key key) const {
- return expectedPosKey == key ? pv[2] : MOVE_NONE;
- }
-
- void update(Position& pos, const std::vector<Move>& newPv) {
-
- assert(newPv.size() >= 3);
-
- // Keep track of how many times in a row the 3rd ply remains stable
- stableCnt = (newPv[2] == pv[2]) ? stableCnt + 1 : 0;
-
- if (!std::equal(newPv.begin(), newPv.begin() + 3, pv))
- {
- std::copy(newPv.begin(), newPv.begin() + 3, pv);
-
- StateInfo st[2];
- pos.do_move(newPv[0], st[0]);
- pos.do_move(newPv[1], st[1]);
- expectedPosKey = pos.key();
- pos.undo_move(newPv[1]);
- pos.undo_move(newPv[0]);
- }
- }
-
- Key expectedPosKey;
- int stableCnt;
- Move pv[3];
- };
-
- EasyMoveManager EasyMove;
Value DrawValue[COLOR_NB];
template <NodeType NT>
Threads.main()->callsCnt = 0;
Threads.main()->previousScore = VALUE_INFINITE;
+ Threads.main()->previousTimeReduction = 1;
}
// Check if there are threads with a better score than main thread
Thread* bestThread = this;
- if ( !this->easyMovePlayed
- && Options["MultiPV"] == 1
+ if ( Options["MultiPV"] == 1
&& !Limits.depth
&& !Skill(Options["Skill Level"]).enabled()
&& rootMoves[0].pv[0] != MOVE_NONE)
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;
+ Move lastBestMove = MOVE_NONE;
+ Depth lastBestMoveDepth = DEPTH_ZERO;
MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr);
+ double timeReduction = 1.0;
std::memset(ss-4, 0, 7 * sizeof(Stack));
for (int i = 4; i > 0; i--)
if (mainThread)
{
- easyMove = EasyMove.get(rootPos.key());
- EasyMove.clear();
- mainThread->easyMovePlayed = mainThread->failedLow = false;
+ mainThread->failedLow = false;
mainThread->bestMoveChanges = 0;
}
if (!Threads.stop)
completedDepth = rootDepth;
+ if (rootMoves[0].pv[0] != lastBestMove) {
+ lastBestMove = rootMoves[0].pv[0];
+ lastBestMoveDepth = rootDepth;
+ }
+
// Have we found a "mate in x"?
if ( Limits.mate
&& bestValue >= VALUE_MATE_IN_MAX_PLY
if (!Threads.stop && !Threads.stopOnPonderhit)
{
// Stop the search if only one legal move is available, or if all
- // of the available time has been used, or if we matched an easyMove
- // from the previous search and just did a fast verification.
+ // of the available time has been used
const int F[] = { mainThread->failedLow,
bestValue - mainThread->previousScore };
int improvingFactor = std::max(229, std::min(715, 357 + 119 * F[0] - 6 * F[1]));
double unstablePvFactor = 1 + mainThread->bestMoveChanges + thinkHard;
- bool doEasyMove = rootMoves[0].pv[0] == easyMove
- && !thinkHard
- && mainThread->bestMoveChanges < 0.03
- && Time.elapsed() > Time.optimum() * 5 / 44;
+ // if the bestMove is stable over several iterations, reduce time for this move,
+ // the longer the move has been stable, the more.
+ // Use part of the gained time from a previous stable move for the current move.
+ timeReduction = 1;
+ for (int i : {3, 4, 5})
+ if (lastBestMoveDepth * i < completedDepth && !thinkHard)
+ timeReduction *= 1.3;
+ unstablePvFactor *= std::pow(mainThread->previousTimeReduction, 0.51) / timeReduction;
if ( rootMoves.size() == 1
- || Time.elapsed() > Time.optimum() * unstablePvFactor * improvingFactor / 628
- || (mainThread->easyMovePlayed = doEasyMove, doEasyMove))
+ || Time.elapsed() > Time.optimum() * unstablePvFactor * improvingFactor / 628)
{
// If we are allowed to ponder do not stop the search now but
// keep pondering until the GUI sends "ponderhit" or "stop".
Threads.stop = true;
}
}
-
- if (rootMoves[0].pv.size() >= 3)
- EasyMove.update(rootPos, rootMoves[0].pv);
- else
- EasyMove.clear();
}
}
if (!mainThread)
return;
- // Clear any candidate easy move that wasn't stable for the last search
- // iterations; the second condition prevents consecutive fast moves.
- if (EasyMove.stableCnt < 6 || mainThread->easyMovePlayed)
- EasyMove.clear();
+ mainThread->previousTimeReduction = timeReduction;
// If skill level is enabled, swap best PV line with the sub-optimal one
if (skill.enabled())