vector<string> positions;
string ttSize, threads, valStr, posFile, valType;
- int val, secsPerPos, maxDepth, maxNodes;
+ int val, maxTime, maxDepth, maxNodes;
ttSize = argc > 2 ? argv[2] : "128";
threads = argc > 3 ? argv[3] : "1";
Options["Use Search Log"].set_value("true");
Options["Search Log Filename"].set_value("bench.txt");
- secsPerPos = maxDepth = maxNodes = 0;
+ maxTime = maxDepth = maxNodes = 0;
val = atoi(valStr.c_str());
if (valType == "depth" || valType == "perft")
maxDepth = val;
else if (valType == "time")
- secsPerPos = val * 1000;
+ maxTime = val * 1000;
else
maxNodes = val;
for (it = positions.begin(); it != positions.end(); ++it, ++cnt)
{
- Move moves[1] = { MOVE_NONE };
- int dummy[2] = { 0, 0 };
+ Move moves[] = { MOVE_NONE };
Position pos(*it, false, 0);
cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl;
if (valType == "perft")
cerr << "\nPerft " << maxDepth << " result (nodes searched): " << perftCnt << endl << endl;
totalNodes += perftCnt;
} else {
- if (!think(pos, false, false, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
+ if (!think(pos, SearchLimits(0, 0, 0, maxTime, maxDepth, maxNodes, false, false), moves))
break;
totalNodes += pos.nodes_searched();
}
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 11. Decide the new search depth
- // Extensions. Configurable UCI options
- // Array index 0 is used at non-PV nodes, index 1 at PV nodes.
+ // 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];
// 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; }
// 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 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;
#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;
/// 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;
current_search_time(get_system_time());
- ExactMaxTime = maxTime;
- MaxDepth = maxDepth;
- MaxNodes = maxNodes;
- InfiniteSearch = infinite;
- Pondering = ponder;
- UseTimeManagement = !ExactMaxTime && !MaxDepth && !MaxNodes && !InfiniteSearch;
+ 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;
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
+ // Write to log file and keep it open to be accessed during the search
if (UseLogFile)
{
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
+ << "\ninfinite: " << Limits.infinite
+ << " ponder: " << Limits.ponder
+ << " time: " << Limits.time
+ << " increment: " << Limits.increment
+ << " moves to go: " << Limits.movesToGo
<< endl;
}
Move ponderMove = MOVE_NONE;
Move bestMove = id_loop(pos, searchMoves, &ponderMove);
- // Print final search statistics
cout << "info" << speed_to_uci(pos.nodes_searched()) << endl;
+ // Write final search statistics and close log file
if (UseLogFile)
{
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
}
// Iterative deepening loop
- while (++depth <= PLY_MAX && (!MaxDepth || depth <= MaxDepth) && !StopRequest)
+ while (++depth <= PLY_MAX && (!Limits.maxDepth || depth <= Limits.maxDepth) && !StopRequest)
{
Rml.bestMoveChanges = 0;
cout << set960(pos.is_chess960()) << "info depth " << depth << endl;
else if (bestMove != easyMove)
easyMove = MOVE_NONE;
- if (UseTimeManagement && !StopRequest)
+ if (Limits.useTimeManagement() && !StopRequest)
{
// Time to stop?
bool noMoreTime = false;
if (noMoreTime)
{
- if (Pondering)
+ if (Limits.ponder)
StopOnPonderhit = true;
else
break;
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;
}
#include "move.h"
#include "types.h"
+class Position;
+struct SplitPoint;
/// The SearchStack struct keeps track of the information we need to remember
/// from nodes shallower and deeper in the tree during the search. Each
/// search thread has its own array of SearchStack objects, indexed by the
/// current ply.
-struct EvalInfo;
-struct SplitPoint;
struct SearchStack {
int ply;
SplitPoint* sp;
};
-class Position;
+
+/// The SearchLimits struct stores information sent by GUI about available time
+/// to search the current move, maximum depth/time, if we are in analysis mode
+/// or if we have to ponder while is our opponent's side to move.
+
+struct SearchLimits {
+
+ SearchLimits() {}
+ SearchLimits(int t, int i, int mtg, int mt, int md, int mn, bool inf, bool pon)
+ : time(t), increment(i), movesToGo(mtg), maxTime(mt), maxDepth(md),
+ maxNodes(mn), infinite(inf), ponder(pon) {}
+
+ bool useTimeManagement() const { return !(maxTime | maxDepth | maxNodes | int(infinite)); }
+
+ int time, increment, movesToGo, maxTime, maxDepth, maxNodes;
+ bool infinite, ponder;
+};
extern void init_threads();
extern void exit_threads();
extern int64_t perft(Position& pos, Depth depth);
-extern bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[],
- int movesToGo, int maxDepth, int maxNodes, int maxTime, Move searchMoves[]);
+extern bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]);
#endif // !defined(SEARCH_H_INCLUDED)
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-////
-//// Includes
-////
-
#include <cmath>
#include "misc.h"
+#include "search.h"
#include "timeman.h"
#include "ucioption.h"
-////
-//// Local definitions
-////
-
namespace {
/// Constants
}
-////
-//// Functions
-////
-
void TimeManager::pv_instability(int curChanges, int prevChanges) {
unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
+ prevChanges * (optimumSearchTime / 3);
}
-void TimeManager::init(int myTime, int myInc, int movesToGo, int currentPly)
+
+void TimeManager::init(const SearchLimits& limits, int currentPly)
{
/* We support four different kind of time controls:
- Inc == 0 && movesToGo == 0 means: x basetime [sudden death!]
- Inc == 0 && movesToGo != 0 means: (x moves) / (y minutes)
- Inc > 0 && movesToGo == 0 means: x basetime + z inc.
- Inc > 0 && movesToGo != 0 means: (x moves) / (y minutes) + z inc
+ increment == 0 && movesToGo == 0 means: x basetime [sudden death!]
+ increment == 0 && movesToGo != 0 means: x moves in y minutes
+ increment > 0 && movesToGo == 0 means: x basetime + z increment
+ increment > 0 && movesToGo != 0 means: x moves in y minutes + z increment
Time management is adjusted by following UCI parameters:
- emergencyMoveHorizon :Be prepared to always play at least this many moves
- emergencyBaseTime :Always attempt to keep at least this much time (in ms) at clock
- emergencyMoveTime :Plus attempt to keep at least this much time for each remaining emergency move
- minThinkingTime :No matter what, use at least this much thinking before doing the move
+ emergencyMoveHorizon: Be prepared to always play at least this many moves
+ emergencyBaseTime : Always attempt to keep at least this much time (in ms) at clock
+ emergencyMoveTime : Plus attempt to keep at least this much time for each remaining emergency move
+ minThinkingTime : No matter what, use at least this much thinking before doing the move
*/
int hypMTG, hypMyTime, t1, t2;
// Initialize to maximum values but unstablePVExtraTime that is reset
unstablePVExtraTime = 0;
- optimumSearchTime = maximumSearchTime = myTime;
+ optimumSearchTime = maximumSearchTime = limits.time;
// We calculate optimum time usage for different hypothetic "moves to go"-values and choose the
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
- for (hypMTG = 1; hypMTG <= (movesToGo ? Min(movesToGo, MoveHorizon) : MoveHorizon); hypMTG++)
+ for (hypMTG = 1; hypMTG <= (limits.movesToGo ? Min(limits.movesToGo, MoveHorizon) : MoveHorizon); hypMTG++)
{
// Calculate thinking time for hypothetic "moves to go"-value
- hypMyTime = Max(myTime + (hypMTG - 1) * myInc - emergencyBaseTime - Min(hypMTG, emergencyMoveHorizon) * emergencyMoveTime, 0);
+ hypMyTime = limits.time
+ + limits.increment * (hypMTG - 1)
+ - emergencyBaseTime
+ - emergencyMoveTime * Min(hypMTG, emergencyMoveHorizon);
+
+ hypMyTime = Max(hypMyTime, 0);
t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, currentPly);
t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly);
optimumSearchTime = Min(optimumSearchTime, maximumSearchTime);
}
-////
-//// Local functions
-////
namespace {
#if !defined(TIMEMAN_H_INCLUDED)
#define TIMEMAN_H_INCLUDED
+struct SearchLimits;
+
class TimeManager {
public:
- void init(int myTime, int myInc, int movesToGo, int currentPly);
+ void init(const SearchLimits& limits, int currentPly);
void pv_instability(int curChanges, int prevChanges);
int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
int maximum_time() const { return maximumSearchTime; }
bool go(Position& pos, UCIParser& up) {
string token;
- Move searchMoves[MOVES_MAX];
- int movesToGo, depth, nodes, moveTime, numOfMoves;
- bool infinite, ponder;
- int time[2] = {0, 0}, inc[2] = {0, 0};
-
- searchMoves[0] = MOVE_NONE;
- infinite = ponder = false;
- movesToGo = depth = nodes = moveTime = numOfMoves = 0;
+ int time[] = { 0, 0 }, inc[] = { 0, 0 };
+ SearchLimits limits(0, 0, 0, 0, 0, 0, false, false);
+ Move searchMoves[MOVES_MAX] = { MOVE_NONE };
+ Move* cur = searchMoves;
while (up >> token)
{
if (token == "infinite")
- infinite = true;
+ limits.infinite = true;
else if (token == "ponder")
- ponder = true;
+ limits.ponder = true;
else if (token == "wtime")
- up >> time[0];
+ up >> time[WHITE];
else if (token == "btime")
- up >> time[1];
+ up >> time[BLACK];
else if (token == "winc")
- up >> inc[0];
+ up >> inc[WHITE];
else if (token == "binc")
- up >> inc[1];
+ up >> inc[BLACK];
else if (token == "movestogo")
- up >> movesToGo;
+ up >> limits.movesToGo;
else if (token == "depth")
- up >> depth;
+ up >> limits.maxDepth;
else if (token == "nodes")
- up >> nodes;
+ up >> limits.maxNodes;
else if (token == "movetime")
- up >> moveTime;
+ up >> limits.maxTime;
else if (token == "searchmoves")
{
while (up >> token)
- searchMoves[numOfMoves++] = move_from_uci(pos, token);
+ *cur++ = move_from_uci(pos, token);
- searchMoves[numOfMoves] = MOVE_NONE;
+ *cur = MOVE_NONE;
}
}
assert(pos.is_ok());
- return think(pos, infinite, ponder, time, inc, movesToGo,
- depth, nodes, moveTime, searchMoves);
+ limits.time = time[pos.side_to_move()];
+ limits.increment = inc[pos.side_to_move()];
+
+ return think(pos, limits, searchMoves);
}
void perft(Position& pos, UCIParser& up) {