template <bool Fake>
void split(Position& pos, SearchStack* ss, Value* alpha, const Value beta, Value* bestValue,
Depth depth, Move threatMove, int moveCount, MovePicker* mp, bool pvNode);
-
private:
Lock mpLock;
Depth minimumSplitDepth;
void extract_pv_from_tt(Position& pos);
void insert_pv_in_tt(Position& pos);
- std::string pv_info_to_uci(Position& pos, int depth, int selDepth, Value alpha, Value beta, int pvIdx);
-
+ std::string pv_info_to_uci(Position& pos, int depth, int selDepth,
+ Value alpha, Value beta, int pvIdx);
int64_t nodes;
Value pv_score;
Value non_pv_score;
};
- // RootMoveList struct is just a std::vector<> of RootMove objects,
+ // RootMoveList struct is just a vector of RootMove objects,
// with an handful of methods above the standard ones.
struct RootMoveList : public std::vector<RootMove> {
// Step 9. Internal iterative deepening
// Minimum depth for use of internal iterative deepening
- const Depth IIDDepth[2] = { 8 * ONE_PLY /* non-PV */, 5 * ONE_PLY /* PV */};
+ const Depth IIDDepth[] = { 8 * ONE_PLY, 5 * ONE_PLY };
// At Non-PV nodes we do an internal iterative deepening search
// when the static evaluation is bigger then beta - IIDMargin.
// Step 11. Decide the new search depth
- // Extensions. Configurable UCI options
- // Array index 0 is used at non-PV nodes, index 1 at PV nodes.
- Depth CheckExtension[2], PawnPushTo7thExtension[2];
- Depth PassedPawnExtension[2], PawnEndgameExtension[2];
+ // Extensions. Array index 0 is used for non-PV nodes, index 1 for PV nodes
+ const Depth CheckExtension[] = { ONE_PLY / 2, ONE_PLY / 1 };
+ const Depth PawnEndgameExtension[] = { ONE_PLY / 1, ONE_PLY / 1 };
+ const Depth PawnPushTo7thExtension[] = { ONE_PLY / 2, ONE_PLY / 2 };
+ const Depth PassedPawnExtension[] = { DEPTH_ZERO, ONE_PLY / 2 };
// Minimum depth for use of singular extension
- const Depth SingularExtensionDepth[2] = { 8 * ONE_PLY /* non-PV */, 6 * ONE_PLY /* PV */};
+ const Depth SingularExtensionDepth[] = { 8 * ONE_PLY, 6 * ONE_PLY };
// Step 12. Futility pruning
// Futility lookup tables (initialized at startup) and their access functions
Value FutilityMarginsMatrix[16][64]; // [depth][moveNumber]
- int FutilityMoveCountArray[32]; // [depth]
+ int FutilityMoveCountArray[32]; // [depth]
- inline Value futility_margin(Depth d, int mn) { return d < 7 * ONE_PLY ? FutilityMarginsMatrix[Max(d, 1)][Min(mn, 63)] : 2 * VALUE_INFINITE; }
- inline int futility_move_count(Depth d) { return d < 16 * ONE_PLY ? FutilityMoveCountArray[d] : 512; }
+ inline Value futility_margin(Depth d, int mn) {
+
+ return d < 7 * ONE_PLY ? FutilityMarginsMatrix[Max(d, 1)][Min(mn, 63)]
+ : 2 * VALUE_INFINITE;
+ }
+
+ inline int futility_move_count(Depth d) {
+
+ return d < 16 * ONE_PLY ? FutilityMoveCountArray[d] : MAX_MOVES;
+ }
// Step 14. Reduced search
- // Reduction lookup tables (initialized at startup) and their getter functions
+ // Reduction lookup tables (initialized at startup) and their access function
int8_t ReductionMatrix[2][64][64]; // [pv][depth][moveNumber]
template <NodeType PV>
int MultiPV, UCIMultiPV;
// Time management variables
- int SearchStartTime, MaxNodes, MaxDepth, ExactMaxTime;
- bool UseTimeManagement, InfiniteSearch, Pondering, StopOnPonderhit;
- bool FirstRootMove, StopRequest, QuitRequest, AspirationFailLow;
+ bool StopOnPonderhit, FirstRootMove, StopRequest, QuitRequest, AspirationFailLow;
TimeManager TimeMgr;
+ SearchLimits Limits;
// Log file
- bool UseLogFile;
std::ofstream LogFile;
// Skill level adjustment
void update_gains(const Position& pos, Move move, Value before, Value after);
void do_skill_level(Move* best, Move* ponder);
- int current_search_time();
+ int current_search_time(int set = 0);
std::string value_to_uci(Value v);
std::string speed_to_uci(int64_t nodes);
void poll(const Position& pos);
#endif
- // MovePickerExt is an extended MovePicker used to choose at compile time
+ // MovePickerExt is an extended MovePicker class used to choose at compile time
// the proper move source according to the type of node.
template<bool SpNode, bool Root> struct MovePickerExt;
int64_t perft(Position& pos, Depth depth) {
- MoveStack mlist[MOVES_MAX];
+ MoveStack mlist[MAX_MOVES];
StateInfo st;
Move m;
int64_t sum = 0;
/// think() is the external interface to Stockfish's search, and is called when
/// the program receives the UCI 'go' command. It initializes various global
-/// variables, and calls id_loop(). It returns false when a quit command is
+/// variables, and calls id_loop(). It returns false when a "quit" command is
/// received during the search.
-bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[],
- int movesToGo, int maxDepth, int maxNodes, int maxTime, Move searchMoves[]) {
+bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
// Initialize global search-related variables
StopOnPonderhit = StopRequest = QuitRequest = AspirationFailLow = SendSearchedNodes = false;
NodesSincePoll = 0;
- SearchStartTime = get_system_time();
- ExactMaxTime = maxTime;
- MaxDepth = maxDepth;
- MaxNodes = maxNodes;
- InfiniteSearch = infinite;
- Pondering = ponder;
- UseTimeManagement = !ExactMaxTime && !MaxDepth && !MaxNodes && !InfiniteSearch;
+ current_search_time(get_system_time());
+ Limits = limits;
+ TimeMgr.init(Limits, pos.startpos_ply_counter());
+
+ // Set best NodesBetweenPolls interval to avoid lagging under time pressure
+ if (Limits.maxNodes)
+ NodesBetweenPolls = Min(Limits.maxNodes, 30000);
+ else if (Limits.time && Limits.time < 1000)
+ NodesBetweenPolls = 1000;
+ else if (Limits.time && Limits.time < 5000)
+ NodesBetweenPolls = 5000;
+ else
+ NodesBetweenPolls = 30000;
// Look for a book move, only during games, not tests
- if (UseTimeManagement && Options["OwnBook"].value<bool>())
+ if (Limits.useTimeManagement() && Options["OwnBook"].value<bool>())
{
if (Options["Book File"].value<std::string>() != OpeningBook.name())
OpeningBook.open(Options["Book File"].value<std::string>());
Move bookMove = OpeningBook.get_move(pos, Options["Best Book Move"].value<bool>());
if (bookMove != MOVE_NONE)
{
- if (Pondering)
+ if (Limits.ponder)
wait_for_stop_or_ponderhit();
cout << "bestmove " << bookMove << endl;
}
// Read UCI options
- CheckExtension[1] = Options["Check Extension (PV nodes)"].value<Depth>();
- CheckExtension[0] = Options["Check Extension (non-PV nodes)"].value<Depth>();
- PawnPushTo7thExtension[1] = Options["Pawn Push to 7th Extension (PV nodes)"].value<Depth>();
- PawnPushTo7thExtension[0] = Options["Pawn Push to 7th Extension (non-PV nodes)"].value<Depth>();
- PassedPawnExtension[1] = Options["Passed Pawn Extension (PV nodes)"].value<Depth>();
- PassedPawnExtension[0] = Options["Passed Pawn Extension (non-PV nodes)"].value<Depth>();
- PawnEndgameExtension[1] = Options["Pawn Endgame Extension (PV nodes)"].value<Depth>();
- PawnEndgameExtension[0] = Options["Pawn Endgame Extension (non-PV nodes)"].value<Depth>();
- UCIMultiPV = Options["MultiPV"].value<int>();
- SkillLevel = Options["Skill level"].value<int>();
- UseLogFile = Options["Use Search Log"].value<bool>();
+ UCIMultiPV = Options["MultiPV"].value<int>();
+ SkillLevel = Options["Skill level"].value<int>();
read_evaluation_uci_options(pos.side_to_move());
ThreadsMgr[i].maxPly = 0;
}
- // Set thinking time
- int myTime = time[pos.side_to_move()];
- int myIncrement = increment[pos.side_to_move()];
- if (UseTimeManagement)
- TimeMgr.init(myTime, myIncrement, movesToGo, pos.startpos_ply_counter());
-
- // Set best NodesBetweenPolls interval to avoid lagging under time pressure
- if (MaxNodes)
- NodesBetweenPolls = Min(MaxNodes, 30000);
- else if (myTime && myTime < 1000)
- NodesBetweenPolls = 1000;
- else if (myTime && myTime < 5000)
- NodesBetweenPolls = 5000;
- else
- NodesBetweenPolls = 30000;
-
- // Write search information to log file
- if (UseLogFile)
+ // Write to log file and keep it open to be accessed during the search
+ if (Options["Use Search Log"].value<bool>())
{
std::string name = Options["Search Log Filename"].value<std::string>();
LogFile.open(name.c_str(), std::ios::out | std::ios::app);
- LogFile << "\nSearching: " << pos.to_fen()
- << "\ninfinite: " << infinite
- << " ponder: " << ponder
- << " time: " << myTime
- << " increment: " << myIncrement
- << " moves to go: " << movesToGo
- << endl;
+ if (LogFile.is_open())
+ LogFile << "\nSearching: " << pos.to_fen()
+ << "\ninfinite: " << Limits.infinite
+ << " ponder: " << Limits.ponder
+ << " time: " << Limits.time
+ << " increment: " << Limits.increment
+ << " moves to go: " << Limits.movesToGo
+ << endl;
}
// We're ready to start thinking. Call the iterative deepening loop function
Move ponderMove = MOVE_NONE;
Move bestMove = id_loop(pos, searchMoves, &ponderMove);
- // Print final search statistics
cout << "info" << speed_to_uci(pos.nodes_searched()) << endl;
- if (UseLogFile)
+ // Write final search statistics and close log file
+ if (LogFile.is_open())
{
int t = current_search_time();
LogFile << "Nodes: " << pos.nodes_searched()
- << "\nNodes/second: " << (t > 0 ? int(pos.nodes_searched() * 1000 / t) : 0)
+ << "\nNodes/second: " << (t > 0 ? pos.nodes_searched() * 1000 / t : 0)
<< "\nBest move: " << move_to_san(pos, bestMove);
StateInfo st;
// If we are pondering or in infinite search, we shouldn't print the
// best move before we are told to do so.
- if (!StopRequest && (Pondering || InfiniteSearch))
+ if (!StopRequest && (Limits.ponder || Limits.infinite))
wait_for_stop_or_ponderhit();
// Could be MOVE_NONE when searching on a stalemate position
return MOVE_NONE;
}
- // Iterative deepening loop
- while (++depth <= PLY_MAX && (!MaxDepth || depth <= MaxDepth) && !StopRequest)
+ // Iterative deepening loop until requested to stop or target depth reached
+ while (!StopRequest && ++depth <= PLY_MAX && (!Limits.maxDepth || depth <= Limits.maxDepth))
{
Rml.bestMoveChanges = 0;
cout << set960(pos.is_chess960()) << "info depth " << depth << endl;
for (int i = 0; i < Min(UCIMultiPV, (int)Rml.size()); i++)
cout << Rml[i].pv_info_to_uci(pos, depth, selDepth, alpha, beta, i) << endl;
- if (UseLogFile)
+ if (LogFile.is_open())
LogFile << pretty_pv(pos, depth, value, current_search_time(), Rml[0].pv) << endl;
// Init easyMove after first iteration or drop if differs from the best move
else if (bestMove != easyMove)
easyMove = MOVE_NONE;
- if (UseTimeManagement && !StopRequest)
+ // Check for some early stop condition
+ if (!StopRequest && Limits.useTimeManagement())
{
- // Time to stop?
- bool noMoreTime = false;
-
// Stop search early when the last two iterations returned a mate score
if ( depth >= 5
- && abs(bestValues[depth]) >= abs(VALUE_MATE) - 100
- && abs(bestValues[depth - 1]) >= abs(VALUE_MATE) - 100)
- noMoreTime = true;
+ && abs(bestValues[depth]) >= VALUE_MATE_IN_PLY_MAX
+ && abs(bestValues[depth - 1]) >= VALUE_MATE_IN_PLY_MAX)
+ StopRequest = true;
// Stop search early if one move seems to be much better than the
- // others or if there is only a single legal move. In this latter
- // case we search up to Iteration 8 anyway to get a proper score.
+ // others or if there is only a single legal move. Also in the latter
+ // case we search up to some depth anyway to get a proper score.
if ( depth >= 7
&& easyMove == bestMove
&& ( Rml.size() == 1
&& current_search_time() > TimeMgr.available_time() / 16)
||( Rml[0].nodes > (pos.nodes_searched() * 98) / 100
&& current_search_time() > TimeMgr.available_time() / 32)))
- noMoreTime = true;
+ StopRequest = true;
- // Add some extra time if the best move has changed during the last two iterations
+ // Take in account some extra time if the best move has changed
if (depth > 4 && depth < 50)
- TimeMgr.pv_instability(bestMoveChanges[depth], bestMoveChanges[depth-1]);
+ TimeMgr.pv_instability(bestMoveChanges[depth], bestMoveChanges[depth - 1]);
- // Stop search if most of MaxSearchTime is consumed at the end of the
- // iteration. We probably don't have enough time to search the first
- // move at the next iteration anyway.
- if (current_search_time() > (TimeMgr.available_time() * 80) / 128)
- noMoreTime = true;
+ // Stop search if most of available time is already consumed. We probably don't
+ // have enough time to search the first move at the next iteration anyway.
+ if (current_search_time() > (TimeMgr.available_time() * 62) / 100)
+ StopRequest = true;
- if (noMoreTime)
+ // If we are allowed to ponder do not stop the search now but keep pondering
+ if (StopRequest && Limits.ponder)
{
- if (Pondering)
- StopOnPonderhit = true;
- else
- break;
+ StopRequest = false;
+ StopOnPonderhit = true;
}
}
}
- // When using skills fake best and ponder moves with the sub-optimal ones
+ // When using skills overwrite best and ponder moves with the sub-optimal ones
if (SkillLevelEnabled)
{
if (skillBest == MOVE_NONE) // Still unassigned ?
assert(PvNode || alpha == beta - 1);
assert(pos.thread() >= 0 && pos.thread() < ThreadsMgr.active_threads());
- Move movesSearched[MOVES_MAX];
+ Move movesSearched[MAX_MOVES];
int64_t nodes;
StateInfo st;
const TTEntry *tte;
&& pos.type_of_piece_on(move_to(m)) != PAWN
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
- pos.midgame_value_of_piece_on(move_to(m)) == VALUE_ZERO)
- && !move_is_promotion(m)
- && !move_is_ep(m))
+ && !move_is_special(m))
{
result += PawnEndgameExtension[PvNode];
*dangerous = true;
// current_search_time() returns the number of milliseconds which have passed
// since the beginning of the current search.
- int current_search_time() {
+ int current_search_time(int set) {
+
+ static int searchStartTime;
+
+ if (set)
+ searchStartTime = set;
- return get_system_time() - SearchStartTime;
+ return get_system_time() - searchStartTime;
}
std::stringstream s;
if (abs(v) < VALUE_MATE - PLY_MAX * ONE_PLY)
- s << "cp " << int(v) * 100 / int(PawnValueMidgame); // Scale to centipawns
+ s << "cp " << int(v) * 100 / int(PawnValueMidgame); // Scale to centipawns
else
- s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
+ s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
return s.str();
}
if (!std::getline(std::cin, command) || command == "quit")
{
// Quit the program as soon as possible
- Pondering = false;
+ Limits.ponder = false;
QuitRequest = StopRequest = true;
return;
}
{
// Stop calculating as soon as possible, but still send the "bestmove"
// and possibly the "ponder" token when finishing the search.
- Pondering = false;
+ Limits.ponder = false;
StopRequest = true;
}
else if (command == "ponderhit")
// The opponent has played the expected move. GUI sends "ponderhit" if
// we were told to ponder on the same move the opponent has played. We
// should continue searching but switching from pondering to normal search.
- Pondering = false;
+ Limits.ponder = false;
if (StopOnPonderhit)
StopRequest = true;
}
// Should we stop the search?
- if (Pondering)
+ if (Limits.ponder)
return;
bool stillAtFirstMove = FirstRootMove
bool noMoreTime = t > TimeMgr.maximum_time()
|| stillAtFirstMove;
- if ( (UseTimeManagement && noMoreTime)
- || (ExactMaxTime && t >= ExactMaxTime)
- || (MaxNodes && pos.nodes_searched() >= MaxNodes)) // FIXME
+ if ( (Limits.useTimeManagement() && noMoreTime)
+ || (Limits.maxTime && t >= Limits.maxTime)
+ || (Limits.maxNodes && pos.nodes_searched() >= Limits.maxNodes)) // FIXME
StopRequest = true;
}
assert(threadID >= 0 && threadID < MAX_THREADS);
int i;
- bool allFinished = false;
+ bool allFinished;
while (true)
{
// If we are not thinking, wait for a condition to be signaled
// instead of wasting CPU time polling for work.
- while ( threadID >= activeThreads || threads[threadID].state == THREAD_INITIALIZING
+ while ( threadID >= activeThreads
+ || threads[threadID].state == THREAD_INITIALIZING
|| (useSleepingThreads && threads[threadID].state == THREAD_AVAILABLE))
{
assert(!sp || useSleepingThreads);
if (threads[threadID].state == THREAD_INITIALIZING)
threads[threadID].state = THREAD_AVAILABLE;
- // Grab the lock to avoid races with wake_sleeping_thread()
+ // Grab the lock to avoid races with Thread::wake_up()
lock_grab(&threads[threadID].sleepLock);
// If we are master and all slaves have finished do not go to sleep
threads[threadID].state = THREAD_SEARCHING;
- // Copy SplitPoint position and search stack and call search()
+ // Copy split point position and search stack and call search()
// with SplitPoint template parameter set to true.
SearchStack ss[PLY_MAX_PLUS_2];
SplitPoint* tsp = threads[threadID].splitPoint;
// Wake up master thread so to allow it to return from the idle loop in
// case we are the last slave of the split point.
- if (useSleepingThreads && threadID != tsp->master && threads[tsp->master].state == THREAD_AVAILABLE)
+ if ( useSleepingThreads
+ && threadID != tsp->master
+ && threads[tsp->master].state == THREAD_AVAILABLE)
threads[tsp->master].wake_up();
}
}
- // init_threads() is called during startup. It launches all helper threads,
- // and initializes the split point stack and the global locks and condition
- // objects.
+ // init_threads() is called during startup. Initializes locks and condition
+ // variables and launches all threads sending them immediately to sleep.
void ThreadsManager::init_threads() {
int i, arg[MAX_THREADS];
bool ok;
- // Initialize global locks
+ // This flag is needed to properly end the threads when program exits
+ allThreadsShouldExit = false;
+
+ // Threads will sent to sleep as soon as created, only main thread is kept alive
+ activeThreads = 1;
+
lock_init(&mpLock);
for (i = 0; i < MAX_THREADS; i++)
{
+ // Initialize thread and split point locks
lock_init(&threads[i].sleepLock);
cond_init(&threads[i].sleepCond);
- }
- // Initialize splitPoints[] locks
- for (i = 0; i < MAX_THREADS; i++)
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
lock_init(&(threads[i].splitPoints[j].lock));
- // Will be set just before program exits to properly end the threads
- allThreadsShouldExit = false;
-
- // Threads will be put all threads to sleep as soon as created
- activeThreads = 1;
-
- // All threads except the main thread should be initialized to THREAD_INITIALIZING
- threads[0].state = THREAD_SEARCHING;
- for (i = 1; i < MAX_THREADS; i++)
- threads[i].state = THREAD_INITIALIZING;
+ // All threads but first should be set to THREAD_INITIALIZING
+ threads[i].state = (i == 0 ? THREAD_SEARCHING : THREAD_INITIALIZING);
+ }
- // Launch the helper threads
+ // Create and startup the threads
for (i = 1; i < MAX_THREADS; i++)
{
arg[i] = i;
void ThreadsManager::exit_threads() {
- allThreadsShouldExit = true; // Let the woken up threads to exit idle_loop()
+ // Force the woken up threads to exit idle_loop() and hence terminate
+ allThreadsShouldExit = true;
- // Wake up all the threads and waits for termination
- for (int i = 1; i < MAX_THREADS; i++)
+ for (int i = 0; i < MAX_THREADS; i++)
{
- threads[i].wake_up();
- while (threads[i].state != THREAD_TERMINATED) {}
- }
+ // Wake up all the threads and waits for termination
+ if (i != 0)
+ {
+ threads[i].wake_up();
+ while (threads[i].state != THREAD_TERMINATED) {}
+ }
+
+ // Now we can safely destroy the locks and wait conditions
+ lock_destroy(&threads[i].sleepLock);
+ cond_destroy(&threads[i].sleepCond);
- // Now we can safely destroy the locks
- for (int i = 0; i < MAX_THREADS; i++)
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
lock_destroy(&(threads[i].splitPoints[j].lock));
+ }
lock_destroy(&mpLock);
-
- // Now we can safely destroy the wait conditions
- for (int i = 0; i < MAX_THREADS; i++)
- {
- lock_destroy(&threads[i].sleepLock);
- cond_destroy(&threads[i].sleepCond);
- }
}
void RootMoveList::init(Position& pos, Move searchMoves[]) {
- MoveStack mlist[MOVES_MAX];
+ MoveStack mlist[MAX_MOVES];
Move* sm;
clear();