using std::endl;
using std::string;
+SearchLimits Limits;
+std::vector<Move> SearchMoves;
+Position* RootPosition;
+
namespace {
// Set to true to force running with one thread. Used for debugging
int MultiPV, UCIMultiPV, MultiPVIdx;
// Time management variables
- bool StopOnPonderhit, FirstRootMove, StopRequest, QuitRequest, AspirationFailLow;
+ volatile bool StopOnPonderhit, FirstRootMove, StopRequest, AspirationFailLow;
TimeManager TimeMgr;
- SearchLimits Limits;
// Skill level adjustment
int SkillLevel;
string pv_to_uci(const Move pv[], int pvNum, bool chess960);
string pretty_pv(Position& pos, int depth, Value score, int time, Move pv[]);
string depth_to_uci(Depth depth);
- void wait_for_stop_or_ponderhit();
// MovePickerExt template class extends MovePicker and allows to choose at compile
// time the proper moves source according to the type of node. In the default case
/// variables, and calls id_loop(). It returns false when a "quit" command is
/// received during the search.
-bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
+void think() {
static Book book; // Defined static to initialize the PRNG only once
+ Position& pos = *RootPosition;
+
// Save "search start" time and reset elapsed time to zero
elapsed_search_time(get_system_time());
// Initialize global search-related variables
- StopOnPonderhit = StopRequest = QuitRequest = AspirationFailLow = false;
- Limits = limits;
+ StopOnPonderhit = StopRequest = AspirationFailLow = false;
// Set output stream mode: normal or chess960. Castling notation is different
cout << set960(pos.is_chess960());
Move bookMove = book.probe(pos, Options["Best Book Move"].value<bool>());
if (bookMove != MOVE_NONE)
{
- if (Limits.ponder)
- wait_for_stop_or_ponderhit();
+ if (!StopRequest && (Limits.ponder || Limits.infinite))
+ Threads.wait_for_stop_or_ponderhit();
cout << "bestmove " << bookMove << endl;
- return !QuitRequest;
+ return;
}
}
else
Threads.set_timer(100);
- // Start async mode to catch UCI commands sent to us while searching,
- // like "quit", "stop", etc.
- Threads.start_listener();
-
// We're ready to start thinking. Call the iterative deepening loop function
Move ponderMove = MOVE_NONE;
- Move bestMove = id_loop(pos, searchMoves, &ponderMove);
-
- // From now on any UCI command will be read in-sync with Threads.getline()
- Threads.stop_listener();
+ Move bestMove = id_loop(pos, &SearchMoves[0], &ponderMove);
// Stop timer, no need to check for available time any more
Threads.set_timer(0);
pos.undo_move(bestMove); // Return from think() with unchanged position
}
- // If we are pondering or in infinite search, we shouldn't print the best move
+ // When we reach max depth we arrive here even without a StopRequest, but if
+ // we are pondering or in infinite search, we shouldn't print the best move
// before we are told to do so.
- if (Limits.ponder || Limits.infinite)
- wait_for_stop_or_ponderhit();
+ if (!StopRequest && (Limits.ponder || Limits.infinite))
+ Threads.wait_for_stop_or_ponderhit();
// Could be MOVE_NONE when searching on a stalemate position
cout << "bestmove " << bestMove;
cout << " ponder " << ponderMove;
cout << endl;
-
- return !QuitRequest;
}
if (depth > 2 && bestMoveChanges[depth])
bestMoveNeverChanged = false;
- // Check for some early stop condition
- if (!StopRequest && Limits.useTimeManagement())
+ // Do we have time for the next iteration? Can we stop searching now?
+ if (!StopRequest && !StopOnPonderhit && Limits.useTimeManagement())
{
// Take in account some extra time if the best move has changed
if (depth > 4 && depth < 50)
}
// If we are allowed to ponder do not stop the search now but keep pondering
- if (StopRequest && Limits.ponder)
+ if (StopRequest && Limits.ponder) // FIXME Limits.ponder is racy
{
StopRequest = false;
StopOnPonderhit = true;
ValueType vt;
Value bestValue, value, oldAlpha;
Value refinedValue, nullValue, futilityBase, futilityValue;
- bool isPvMove, inCheck, singularExtensionNode, givesCheck, captureOrPromotion, dangerous;
+ bool isPvMove, inCheck, singularExtensionNode, givesCheck;
+ bool captureOrPromotion, dangerous, doFullDepthSearch;
int moveCount = 0, playedMoveCount = 0;
Thread& thread = Threads[pos.thread()];
SplitPoint* sp = NULL;
Value rBeta = ttValue - int(depth);
ss->excludedMove = move;
ss->skipNullMove = true;
- Value v = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2);
+ value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2);
ss->skipNullMove = false;
ss->excludedMove = MOVE_NONE;
ss->bestMove = MOVE_NONE;
- if (v < rBeta)
+ if (value < rBeta)
ext = ONE_PLY;
}
}
// Step 14. Make the move
pos.do_move(move, st, ci, givesCheck);
- // Step extra. pv search (only in PV nodes)
- // The first move in list is the expected PV
- if (isPvMove)
- value = newDepth < ONE_PLY ? -qsearch<PV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
- : - search<PV>(pos, ss+1, -beta, -alpha, newDepth);
- else
+ // Step 15. Reduced depth search (LMR). If the move fails high will be
+ // re-searched at full depth.
+ if ( depth > 3 * ONE_PLY
+ && !isPvMove
+ && !captureOrPromotion
+ && !dangerous
+ && !is_castle(move)
+ && ss->killers[0] != move
+ && ss->killers[1] != move)
{
- // Step 15. Reduced depth search
- // If the move fails high will be re-searched at full depth.
- bool doFullDepthSearch = true;
-
- if ( depth > 3 * ONE_PLY
- && !captureOrPromotion
- && !dangerous
- && !is_castle(move)
- && ss->killers[0] != move
- && ss->killers[1] != move
- && (ss->reduction = reduction<PvNode>(depth, moveCount)) != DEPTH_ZERO)
- {
- Depth d = newDepth - ss->reduction;
- alpha = SpNode ? sp->alpha : alpha;
+ ss->reduction = reduction<PvNode>(depth, moveCount);
+ Depth d = newDepth - ss->reduction;
+ alpha = SpNode ? sp->alpha : alpha;
- value = d < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
- : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d);
+ value = d < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
+ : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d);
- ss->reduction = DEPTH_ZERO;
- doFullDepthSearch = (value > alpha);
- }
+ doFullDepthSearch = (value > alpha && ss->reduction != DEPTH_ZERO);
+ ss->reduction = DEPTH_ZERO;
+ }
+ else
+ doFullDepthSearch = !isPvMove;
- // Step 16. Full depth search
- if (doFullDepthSearch)
- {
- alpha = SpNode ? sp->alpha : alpha;
- value = newDepth < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
- : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth);
-
- // Step extra. pv search (only in PV nodes)
- // Search only for possible new PV nodes, if instead value >= beta then
- // parent node fails low with value <= alpha and tries another move.
- if (PvNode && value > alpha && (RootNode || value < beta))
- value = newDepth < ONE_PLY ? -qsearch<PV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
- : - search<PV>(pos, ss+1, -beta, -alpha, newDepth);
- }
+ // Step 16. Full depth search, when LMR is skipped or fails high
+ if (doFullDepthSearch)
+ {
+ alpha = SpNode ? sp->alpha : alpha;
+ value = newDepth < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
+ : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth);
}
+ // Only for PV nodes do a full PV search on the first move or after a fail
+ // high, in the latter case search only if value < beta, otherwise let the
+ // parent node to fail low with value <= alpha and to try another move.
+ if (PvNode && (isPvMove || (value > alpha && (RootNode || value < beta))))
+ value = newDepth < ONE_PLY ? -qsearch<PV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
+ : - search<PV>(pos, ss+1, -beta, -alpha, newDepth);
+
// Step 17. Undo move
pos.undo_move(move);
}
- // wait_for_stop_or_ponderhit() is called when the maximum depth is reached
- // while the program is pondering. The point is to work around a wrinkle in
- // the UCI protocol: When pondering, the engine is not allowed to give a
- // "bestmove" before the GUI sends it a "stop" or "ponderhit" command.
- // We simply wait here until one of these commands (that raise StopRequest) is
- // sent, and return, after which the bestmove and pondermove will be printed.
-
- void wait_for_stop_or_ponderhit() {
-
- string cmd;
- StopOnPonderhit = true;
-
- while (!StopRequest)
- {
- Threads.getline(cmd);
- do_uci_async_cmd(cmd);
- }
- }
-
-
// When playing with strength handicap choose best move among the MultiPV set
// using a statistical rule dependent on SkillLevel. Idea by Heinz van Saanen.
}
-// do_uci_async_cmd() is called by listener thread when in async mode and 'cmd'
-// input line is received from the GUI.
+// ThreadsManager::wait_for_stop_or_ponderhit() is called when the maximum depth
+// is reached while the program is pondering. The point is to work around a wrinkle
+// in the UCI protocol: When pondering, the engine is not allowed to give a
+// "bestmove" before the GUI sends it a "stop" or "ponderhit" command.
+// We simply wait here until one of these commands (that raise StopRequest) is
+// sent, and return, after which the bestmove and pondermove will be printed.
+
+void ThreadsManager::wait_for_stop_or_ponderhit() {
+
+ StopOnPonderhit = true;
+
+ Thread& main = threads[0];
+
+ lock_grab(&main.sleepLock);
+
+ while (!StopRequest)
+ cond_wait(&main.sleepCond, &main.sleepLock);
+
+ lock_release(&main.sleepLock);
+}
+
-void do_uci_async_cmd(const std::string& cmd) {
+// uci_async_command() is called when a 'cmd' input line is received from the
+// GUI while searching.
- if (cmd == "quit")
- QuitRequest = StopRequest = true;
+void uci_async_command(const std::string& cmd) {
- else if (cmd == "stop")
+ if (cmd == "quit" || cmd == "stop")
StopRequest = true;
else if (cmd == "ponderhit")
static int lastInfoTime;
int e = elapsed_search_time();
- // Print debug information every second
- if (get_system_time() - lastInfoTime >= 1000)
+ // Print debug information every one second
+ if (!lastInfoTime || get_system_time() - lastInfoTime >= 1000)
{
lastInfoTime = get_system_time();