bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
- static Book book; // Define static to initialize the PRNG only once
+ static Book book; // Defined static to initialize the PRNG only once
+
+ // 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;
- elapsed_search_time(get_system_time());
Limits = limits;
- TimeMgr.init(Limits, pos.startpos_ply_counter());
- // Set output steram in normal or chess960 mode
+ // Set output stream mode: normal or chess960. Castling notation is different
cout << set960(pos.is_chess960());
// Look for a book move
}
}
- // Set best timer interval to avoid lagging under time pressure
- if (TimeMgr.available_time())
- Threads.set_timer(std::min(100, std::max(TimeMgr.available_time() / 8, 20)));
- else
- Threads.set_timer(100);
-
- // Read UCI options
- UCIMultiPV = Options["MultiPV"].value<int>();
- SkillLevel = Options["Skill Level"].value<int>();
-
+ // Read UCI options: GUI could change UCI parameters during the game
read_evaluation_uci_options(pos.side_to_move());
Threads.read_uci_options();
TT.clear();
}
+ UCIMultiPV = Options["MultiPV"].value<int>();
+ SkillLevel = Options["Skill Level"].value<int>();
+
// Do we have to play with skill handicap? In this case enable MultiPV that
// we will use behind the scenes to retrieve a set of possible moves.
SkillLevelEnabled = (SkillLevel < 20);
MultiPV = (SkillLevelEnabled ? std::max(UCIMultiPV, 4) : UCIMultiPV);
- // Wake up needed threads and reset maxPly counter
- for (int i = 0; i < Threads.size(); i++)
- {
- Threads[i].wake_up();
- Threads[i].maxPly = 0;
- }
-
- // Write to log file and keep it open to be accessed during the search
+ // Write current search header to log file
if (Options["Use Search Log"].value<bool>())
{
Log log(Options["Search Log Filename"].value<string>());
<< endl;
}
+ // Wake up needed threads and reset maxPly counter
+ for (int i = 0; i < Threads.size(); i++)
+ {
+ Threads[i].maxPly = 0;
+ Threads[i].wake_up();
+ }
+
+ // Set best timer interval to avoid lagging under time pressure. Timer is
+ // used to check for remaining available thinking time.
+ TimeMgr.init(Limits, pos.startpos_ply_counter());
+
+ if (TimeMgr.available_time())
+ Threads.set_timer(std::min(100, std::max(TimeMgr.available_time() / 8, 20)));
+ else
+ Threads.set_timer(100);
+
// Start async mode to catch UCI commands sent to us while searching,
// like "quit", "stop", etc.
Threads.start_listener();
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();
+
+ // Stop timer, no need to check for available time any more
Threads.set_timer(0);
- // Write final search statistics and close log file
+ // This makes all the slave threads to go to sleep, if not already sleeping
+ Threads.set_size(1);
+
+ // Write current search final statistics to log file
if (Options["Use Search Log"].value<bool>())
{
int e = elapsed_search_time();
pos.undo_move(bestMove); // Return from think() with unchanged position
}
- // This makes all the threads to go to sleep
- Threads.set_size(1);
-
- // From now on any UCI command will be read in-sync with Threads.getline()
- Threads.stop_listener();
-
- // If we are pondering or in infinite search, we shouldn't print the
- // best move before we are told to do so.
- if (!StopRequest && (Limits.ponder || Limits.infinite))
+ // 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();
// Could be MOVE_NONE when searching on a stalemate position
return s.str();
}
+
// pv_to_uci() returns a string with information on the current PV line
// formatted according to UCI specification.
return s.str();
}
+
// depth_to_uci() returns a string with information on the current depth and
// seldepth formatted according to UCI specification.
return s.str();
}
+
// pretty_pv() creates a human-readable string from a position and a PV.
// It is used to write search information to the log file (which is created
// when the UCI parameter "Use Search Log" is "true").
// 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 is sent, and return,
- // after which the bestmove and pondermove will be printed.
+ // 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;
- // Wait for a command from stdin
- while (cmd != "ponderhit" && cmd != "stop" && cmd != "quit")
+ while (!StopRequest)
+ {
Threads.getline(cmd);
-
- if (cmd == "quit")
- QuitRequest = true;
+ 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.
+
void do_skill_level(Move* best, Move* ponder) {
assert(MultiPV > 1);
return NULL;
}
+
// extract_pv_from_tt() builds a PV by adding moves from the transposition table.
// We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes. This
// allow to always have a ponder move even when we fail high at root and also a
do pos.undo_move(pv[--ply]); while (ply);
}
+
// insert_pv_in_tt() is called at the end of a search iteration, and inserts
// the PV back into the TT. This makes sure the old PV moves are searched
// first, even if the old TT entries have been overwritten.
do pos.undo_move(pv[--ply]); while (ply);
}
-} // namespace
-
-
-// Little helper used by idle_loop() to check that all the slave threads of a
-// split point have finished searching.
-
-static bool all_slaves_finished(SplitPoint* sp) {
- for (int i = 0; i < Threads.size(); i++)
- if (sp->is_slave[i])
- return false;
-
- return true;
-}
+} // namespace
// Thread::idle_loop() is where the thread is parked when it has no work to do.
lock_grab(&sleepLock);
// If we are master and all slaves have finished don't go to sleep
- if (sp && all_slaves_finished(sp))
+ if (sp && Threads.split_point_finished(sp))
{
lock_release(&sleepLock);
break;
// If this thread is the master of a split point and all slaves have
// finished their work at this split point, return from the idle loop.
- if (sp && all_slaves_finished(sp))
+ if (sp && Threads.split_point_finished(sp))
{
// Because sp->is_slave[] is reset under lock protection,
// be sure sp->lock has been released before to return.
void do_uci_async_cmd(const std::string& cmd) {
if (cmd == "quit")
- {
- // Quit the program as soon as possible
- Limits.ponder = false;
QuitRequest = StopRequest = true;
- }
+
else if (cmd == "stop")
- {
- // Stop calculating as soon as possible, but still send the "bestmove"
- // and possibly the "ponder" token when finishing the search.
- Limits.ponder = false;
StopRequest = true;
- }
+
else if (cmd == "ponderhit")
{
// The opponent has played the expected move. GUI sends "ponderhit" if