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
Value bestValues[PLY_MAX_PLUS_2];
int bestMoveChanges[PLY_MAX_PLUS_2];
int depth, aspirationDelta;
- Value value, alpha, beta;
+ Value bestValue, alpha, beta;
Move bestMove, easyMove, skillBest, skillPonder;
// Initialize stuff before a new search
H.clear();
*ponderMove = bestMove = easyMove = skillBest = skillPonder = MOVE_NONE;
depth = aspirationDelta = 0;
- value = alpha = -VALUE_INFINITE, beta = VALUE_INFINITE;
+ bestValue = alpha = -VALUE_INFINITE, beta = VALUE_INFINITE;
ss->currentMove = MOVE_NULL; // Hack to skip update gains
// Moves to search are verified and copied
do {
// Search starts from ss+1 to allow referencing (ss-1). This is
// needed by update gains and ss copy when splitting at Root.
- value = search<Root>(pos, ss+1, alpha, beta, depth * ONE_PLY);
+ bestValue = search<Root>(pos, ss+1, alpha, beta, depth * ONE_PLY);
// Bring to front the best move. It is critical that sorting is
// done with a stable algorithm because all the values but the first
// the fail high/low loop then reorder the PV moves, otherwise
// leave the last PV move in its position so to be searched again.
// Of course this is needed only in MultiPV search.
- if (MultiPVIdx && value > alpha && value < beta)
+ if (MultiPVIdx && bestValue > alpha && bestValue < beta)
sort<RootMove>(Rml.begin(), Rml.begin() + MultiPVIdx);
// Write PV back to transposition table in case the relevant entries
// if we have a fail high/low and we are deep in the search. UCI
// protocol requires to send all the PV lines also if are still
// to be searched and so refer to the previous search's score.
- if ((value > alpha && value < beta) || elapsed_search_time() > 2000)
+ if ((bestValue > alpha && bestValue < beta) || elapsed_search_time() > 2000)
for (int i = 0; i < std::min(UCIMultiPV, (int)Rml.size()); i++)
{
bool updated = (i <= MultiPVIdx);
// In case of failing high/low increase aspiration window and
// research, otherwise exit the fail high/low loop.
- if (value >= beta)
+ if (bestValue >= beta)
{
beta = std::min(beta + aspirationDelta, VALUE_INFINITE);
aspirationDelta += aspirationDelta / 2;
}
- else if (value <= alpha)
+ else if (bestValue <= alpha)
{
AspirationFailLow = true;
StopOnPonderhit = false;
else
break;
- } while (abs(value) < VALUE_KNOWN_WIN);
+ } while (abs(bestValue) < VALUE_KNOWN_WIN);
}
// Collect info about search result
bestMove = Rml[0].pv[0];
*ponderMove = Rml[0].pv[1];
- bestValues[depth] = value;
+ bestValues[depth] = bestValue;
bestMoveChanges[depth] = Rml.bestMoveChanges;
// Skills: Do we need to pick now the best and the ponder moves ?
if (Options["Use Search Log"].value<bool>())
{
Log log(Options["Search Log Filename"].value<string>());
- log << pretty_pv(pos, depth, value, elapsed_search_time(), &Rml[0].pv[0]) << endl;
+ log << pretty_pv(pos, depth, bestValue, elapsed_search_time(), &Rml[0].pv[0]) << endl;
}
// Init easyMove at first iteration or drop it if differs from the best move
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