#include <algorithm>
#include <cassert>
-#include <cfloat>
#include <cmath>
#include <cstring>
#include <iostream>
/// Search::perft() is our utility to verify move generation. All the leaf nodes
/// up to the given depth are generated and counted and the sum returned.
-
-static uint64_t perft(Position& pos, Depth depth) {
+template<bool Root>
+uint64_t Search::perft(Position& pos, Depth depth) {
StateInfo st;
- uint64_t cnt = 0;
+ uint64_t cnt, nodes = 0;
CheckInfo ci(pos);
const bool leaf = depth == 2 * ONE_PLY;
for (MoveList<LEGAL> it(pos); *it; ++it)
{
- pos.do_move(*it, st, ci, pos.gives_check(*it, ci));
- cnt += leaf ? MoveList<LEGAL>(pos).size() : ::perft(pos, depth - ONE_PLY);
- pos.undo_move(*it);
+ if (Root && depth <= ONE_PLY)
+ cnt = 1, nodes++;
+ else
+ {
+ pos.do_move(*it, st, ci, pos.gives_check(*it, ci));
+ cnt = leaf ? MoveList<LEGAL>(pos).size() : perft<false>(pos, depth - ONE_PLY);
+ nodes += cnt;
+ pos.undo_move(*it);
+ }
+ if (Root)
+ sync_cout << move_to_uci(*it, pos.is_chess960()) << ": " << cnt << sync_endl;
}
- return cnt;
+ return nodes;
}
-uint64_t Search::perft(Position& pos, Depth depth) {
- return depth > ONE_PLY ? ::perft(pos, depth) : MoveList<LEGAL>(pos).size();
-}
+template uint64_t Search::perft<true>(Position& pos, Depth depth);
+
/// Search::think() is the external interface to Stockfish's search, and is
/// called by the main thread when the program receives the UCI 'go' command. It
goto finalize;
}
- if (Options["Write Search Log"])
- {
- Log log(Options["Search Log Filename"]);
- log << "\nSearching: " << RootPos.fen()
- << "\ninfinite: " << Limits.infinite
- << " ponder: " << Limits.ponder
- << " time: " << Limits.time[RootPos.side_to_move()]
- << " increment: " << Limits.inc[RootPos.side_to_move()]
- << " moves to go: " << Limits.movestogo
- << "\n" << std::endl;
- }
-
// Reset the threads, still sleeping: will wake up at split time
for (size_t i = 0; i < Threads.size(); ++i)
Threads[i]->maxPly = 0;
Threads.timer->run = false; // Stop the timer
- if (Options["Write Search Log"])
- {
- Time::point elapsed = Time::now() - SearchTime + 1;
-
- Log log(Options["Search Log Filename"]);
- log << "Nodes: " << RootPos.nodes_searched()
- << "\nNodes/second: " << RootPos.nodes_searched() * 1000 / elapsed
- << "\nBest move: " << move_to_san(RootPos, RootMoves[0].pv[0]);
-
- StateInfo st;
- RootPos.do_move(RootMoves[0].pv[0], st);
- log << "\nPonder move: " << move_to_san(RootPos, RootMoves[0].pv[1]) << std::endl;
- RootPos.undo_move(RootMoves[0].pv[0]);
- }
-
finalize:
// When search is stopped this info is not printed
RootMoves[i].prevScore = RootMoves[i].score;
// MultiPV loop. We perform a full root search for each PV line
- for (PVIdx = 0; PVIdx < multiPV && PVIdx < RootMoves.size() && !Signals.stop; ++PVIdx)
+ for (PVIdx = 0; PVIdx < std::min(multiPV, RootMoves.size()) && !Signals.stop; ++PVIdx)
{
// Reset aspiration window starting size
if (depth >= 5)
// Sort the PV lines searched so far and update the GUI
std::stable_sort(RootMoves.begin(), RootMoves.begin() + PVIdx + 1);
- if (PVIdx + 1 == multiPV || Time::now() - SearchTime > 3000)
+ if (PVIdx + 1 == std::min(multiPV, RootMoves.size()) || Time::now() - SearchTime > 3000)
sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl;
}
if (skill.candidates_size() && skill.time_to_pick(depth))
skill.pick_move();
- if (Options["Write Search Log"])
- {
- RootMove& rm = RootMoves[0];
- if (skill.best != MOVE_NONE)
- rm = *std::find(RootMoves.begin(), RootMoves.end(), skill.best);
-
- Log log(Options["Search Log Filename"]);
- log << pretty_pv(pos, depth, rm.score, Time::now() - SearchTime, &rm.pv[0])
- << std::endl;
- }
-
// Have we found a "mate in x"?
if ( Limits.mate
&& bestValue >= VALUE_MATE_IN_MAX_PLY
assert(!this_sp || (this_sp->masterThread == this && searching));
- while (true)
+ while (!exit)
{
- // If we are not searching, wait for a condition to be signaled instead of
- // wasting CPU time polling for work.
- while (!searching || exit)
- {
- if (exit)
- {
- assert(!this_sp);
- return;
- }
-
- // Grab the lock to avoid races with Thread::notify_one()
- mutex.lock();
-
- // If we are master and all slaves have finished then exit idle_loop
- if (this_sp && this_sp->slavesMask.none())
- {
- mutex.unlock();
- break;
- }
-
- // Do sleep after retesting sleep conditions under lock protection. In
- // particular we need to avoid a deadlock in case a master thread has,
- // in the meanwhile, allocated us and sent the notify_one() call before
- // we had the chance to grab the lock.
- if (!searching && !exit)
- sleepCondition.wait(mutex);
-
- mutex.unlock();
- }
-
// If this thread has been assigned work, launch a search
- if (searching)
+ while (searching)
{
- assert(!exit);
-
Threads.mutex.lock();
- assert(searching);
assert(activeSplitPoint);
SplitPoint* sp = activeSplitPoint;
}
}
- // 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.
+ // Grab the lock to avoid races with Thread::notify_one()
+ mutex.lock();
+
+ // If we are master and all slaves have finished then exit idle_loop
if (this_sp && this_sp->slavesMask.none())
{
- this_sp->mutex.lock();
- bool finished = this_sp->slavesMask.none(); // Retest under lock protection
- this_sp->mutex.unlock();
- if (finished)
- return;
+ assert(!searching);
+ mutex.unlock();
+ break;
}
+
+ // If we are not searching, wait for a condition to be signaled instead of
+ // wasting CPU time polling for work.
+ if (!searching && !exit)
+ sleepCondition.wait(mutex);
+
+ mutex.unlock();
}
}