Options["Threads"] = threads;
if (valType == "time")
- limits.maxTime = 1000 * atoi(valStr.c_str()); // maxTime is in ms
+ limits.movetime = 1000 * atoi(valStr.c_str()); // movetime is in ms
else if (valType == "nodes")
- limits.maxNodes = atoi(valStr.c_str());
+ limits.nodes = atoi(valStr.c_str());
else
- limits.maxDepth = atoi(valStr.c_str());
+ limits.depth = atoi(valStr.c_str());
if (fenFile != "default")
{
if (valType == "perft")
{
- int64_t cnt = Search::perft(pos, limits.maxDepth * ONE_PLY);
- cerr << "\nPerft " << limits.maxDepth << " leaf nodes: " << cnt << endl;
+ int64_t cnt = Search::perft(pos, limits.depth * ONE_PLY);
+ cerr << "\nPerft " << limits.depth << " leaf nodes: " << cnt << endl;
nodes += cnt;
}
else
{
- Threads.start_thinking(pos, limits);
+ Threads.start_searching(pos, limits);
+ Threads.wait_for_search_finished();
nodes += Search::RootPosition.nodes_searched();
}
}
// happen in Chess960 games.
const Score TrappedBishopA1H1Penalty = make_score(100, 100);
- // Penalty for a minor piece that is not defended by anything
- const Score UndefendedMinorPenalty = make_score(25, 10);
+ // Penalty for BNR that is not defended by anything
+ const Score UndefendedPiecePenalty = make_score(25, 10);
// The SpaceMask[Color] contains the area of the board which is considered
// by the space evaluation. In the middle game, each side is given a bonus
const Color Them = (Us == WHITE ? BLACK : WHITE);
- Bitboard b;
+ Bitboard b, undefended, undefendedMinors, weakEnemies;
Score score = SCORE_ZERO;
- // Undefended minors get penalized even if not under attack
- Bitboard undefended = pos.pieces(Them)
- & (pos.pieces(BISHOP) | pos.pieces(KNIGHT))
- & ~ei.attackedBy[Them][0];
- if (undefended)
- score += single_bit(undefended) ? UndefendedMinorPenalty
- : UndefendedMinorPenalty * 2;
+ // Undefended pieces get penalized even if not under attack
+ undefended = pos.pieces(Them) & ~ei.attackedBy[Them][0];
+ undefendedMinors = undefended & (pos.pieces(BISHOP) | pos.pieces(KNIGHT));
+
+ if (undefendedMinors)
+ score += single_bit(undefendedMinors) ? UndefendedPiecePenalty
+ : UndefendedPiecePenalty * 2;
+ if (undefended & pos.pieces(ROOK))
+ score += UndefendedPiecePenalty;
// Enemy pieces not defended by a pawn and under our attack
- Bitboard weakEnemies = pos.pieces(Them)
- & ~ei.attackedBy[Them][PAWN]
- & ei.attackedBy[Us][0];
+ weakEnemies = pos.pieces(Them)
+ & ~ei.attackedBy[Them][PAWN]
+ & ei.attackedBy[Us][0];
+
if (!weakEnemies)
return score;
Chess960 = pos.is_chess960();
Eval::RootColor = pos.side_to_move();
SearchTime.restart();
- TimeMgr.init(Limits, pos.startpos_ply_counter());
+ TimeMgr.init(Limits, pos.startpos_ply_counter(), pos.side_to_move());
TT.new_search();
H.clear();
log << "\nSearching: " << pos.to_fen()
<< "\ninfinite: " << Limits.infinite
<< " ponder: " << Limits.ponder
- << " time: " << Limits.time
- << " increment: " << Limits.increment
- << " moves to go: " << Limits.movesToGo
+ << " time: " << Limits.times[pos.side_to_move()]
+ << " increment: " << Limits.incs[pos.side_to_move()]
+ << " moves to go: " << Limits.movestogo
<< endl;
}
ss->currentMove = MOVE_NULL; // Hack to skip update gains
// Iterative deepening loop until requested to stop or target depth reached
- while (!Signals.stop && ++depth <= MAX_PLY && (!Limits.maxDepth || depth <= Limits.maxDepth))
+ while (!Signals.stop && ++depth <= MAX_PLY && (!Limits.depth || depth <= Limits.depth))
{
// Save last iteration's scores before first PV line is searched and all
// the move scores but the (new) PV are set to -VALUE_INFINITE.
// Step 2. Check for aborted search and immediate draw
// Enforce node limit here. FIXME: This only works with 1 search thread.
- if (Limits.maxNodes && pos.nodes_searched() >= Limits.maxNodes)
+ if (Limits.nodes && pos.nodes_searched() >= Limits.nodes)
Signals.stop = true;
if (( Signals.stop
|| stillAtFirstMove;
if ( (Limits.use_time_management() && noMoreTime)
- || (Limits.maxTime && e >= Limits.maxTime))
+ || (Limits.movetime && e >= Limits.movetime))
Signals.stop = true;
}
struct LimitsType {
LimitsType() { memset(this, 0, sizeof(LimitsType)); }
- bool use_time_management() const { return !(maxTime | maxDepth | maxNodes | infinite); }
+ bool use_time_management() const { return !(movetime | depth | nodes | infinite); }
- int time, increment, movesToGo, maxTime, maxDepth, maxNodes, infinite, ponder;
+ int times[2], incs[2], movestogo, depth, nodes, movetime, infinite, ponder;
};
start_fn = fn;
threadID = Threads.size();
- do_sleep = (fn != &Thread::main_loop); // Avoid a race with start_thinking()
+ do_sleep = (fn != &Thread::main_loop); // Avoid a race with start_searching()
lock_init(sleepLock);
cond_init(sleepCond);
Signals.stopOnPonderhit = true;
lock_grab(sleepLock);
-
- while (!Signals.stop)
- cond_wait(sleepCond, sleepLock);
-
+ while (!Signals.stop) cond_wait(sleepCond, sleepLock);
lock_release(sleepLock);
}
for (int i = 0; i < size(); i++)
{
- threads[i]->do_sleep = false;
threads[i]->maxPly = 0;
+ threads[i]->do_sleep = false;
if (!useSleepingThreads)
threads[i]->wake_up();
void ThreadsManager::sleep() const {
for (int i = 1; i < size(); i++) // Main thread will go to sleep by itself
- threads[i]->do_sleep = true; // to avoid a race with start_thinking()
+ threads[i]->do_sleep = true; // to avoid a race with start_searching()
}
}
-// ThreadsManager::start_thinking() is used by UI thread to wake up the main
-// thread parked in main_loop() and starting a new search. If async is true
-// then function returns immediately, otherwise caller is blocked waiting for
-// the search to finish.
+// ThreadsManager::wait_for_search_finished() waits for main thread to go to
+// sleep, this means search is finished. Then returns.
+
+void ThreadsManager::wait_for_search_finished() {
+
+ Thread* main = threads[0];
+ lock_grab(main->sleepLock);
+ while (!main->do_sleep) cond_wait(sleepCond, main->sleepLock);
+ lock_release(main->sleepLock);
+}
-void ThreadsManager::start_thinking(const Position& pos, const LimitsType& limits,
- const std::set<Move>& searchMoves, bool async) {
- Thread& main = *threads.front();
- lock_grab(main.sleepLock);
+// ThreadsManager::start_searching() wakes up the main thread sleeping in
+// main_loop() so to start a new search, then returns immediately.
- while (!main.do_sleep)
- cond_wait(sleepCond, main.sleepLock); // Wait main thread has finished
+void ThreadsManager::start_searching(const Position& pos, const LimitsType& limits,
+ const std::set<Move>& searchMoves) {
+ wait_for_search_finished();
Signals.stopOnPonderhit = Signals.firstRootMove = false;
Signals.stop = Signals.failedLowAtRoot = false;
if (searchMoves.empty() || searchMoves.count(ml.move()))
RootMoves.push_back(RootMove(ml.move()));
- main.do_sleep = false;
- cond_signal(main.sleepCond); // Wake up main thread and start searching
-
- if (!async)
- while (!main.do_sleep)
- cond_wait(sleepCond, main.sleepLock);
-
- lock_release(main.sleepLock);
-}
-
-
-// ThreadsManager::stop_thinking() is used by UI thread to raise a stop request
-// and to wait for the main thread finishing the search. We cannot return before
-// main has finished to avoid a crash in case of a 'quit' command.
-
-void ThreadsManager::stop_thinking() {
-
- Thread& main = *threads.front();
-
- Search::Signals.stop = true;
-
- lock_grab(main.sleepLock);
-
- cond_signal(main.sleepCond); // In case is waiting for stop or ponderhit
-
- while (!main.do_sleep)
- cond_wait(sleepCond, main.sleepLock);
-
- lock_release(main.sleepLock);
+ threads[0]->do_sleep = false;
+ threads[0]->wake_up();
}
MovePicker* mp;
SplitPoint* parent;
-
// Shared data
Lock lock;
volatile uint64_t slavesMask;
void read_uci_options();
bool available_slave_exists(int master) const;
void set_timer(int msec);
- void stop_thinking();
- void start_thinking(const Position& pos, const Search::LimitsType& limits,
- const std::set<Move>& = std::set<Move>(), bool async = false);
+ void wait_for_search_finished();
+ void start_searching(const Position& pos, const Search::LimitsType& limits,
+ const std::set<Move>& = std::set<Move>());
template <bool Fake>
Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Move* bestMove,
}
-void TimeManager::init(const Search::LimitsType& limits, int currentPly)
+void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color us)
{
/* We support four different kind of time controls:
// Initialize to maximum values but unstablePVExtraTime that is reset
unstablePVExtraTime = 0;
- optimumSearchTime = maximumSearchTime = limits.time;
+ optimumSearchTime = maximumSearchTime = limits.times[us];
// 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 <= (limits.movesToGo ? std::min(limits.movesToGo, MoveHorizon) : MoveHorizon); hypMTG++)
+ for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); hypMTG++)
{
// Calculate thinking time for hypothetic "moves to go"-value
- hypMyTime = limits.time
- + limits.increment * (hypMTG - 1)
+ hypMyTime = limits.times[us]
+ + limits.incs[us] * (hypMTG - 1)
- emergencyBaseTime
- emergencyMoveTime * std::min(hypMTG, emergencyMoveHorizon);
class TimeManager {
public:
- void init(const Search::LimitsType& limits, int currentPly);
+ void init(const Search::LimitsType& limits, int currentPly, Color us);
void pv_instability(int curChanges, int prevChanges);
int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
int maximum_time() const { return maximumSearchTime; }
is >> skipws >> token;
if (token == "quit" || token == "stop")
- Threads.stop_thinking();
+ {
+ Search::Signals.stop = true;
+
+ if (token == "quit") // Cannot quit while threads are still running
+ Threads.wait_for_search_finished();
+ }
else if (token == "ponderhit")
{
Search::Limits.ponder = false;
if (Search::Signals.stopOnPonderhit)
- Threads.stop_thinking();
+ Search::Signals.stop = true;
}
else if (token == "go")
// go() is called when engine receives the "go" UCI command. The function sets
// the thinking time and other parameters from the input string, and then starts
- // the main searching thread.
+ // the search.
void go(Position& pos, istringstream& is) {
- string token;
Search::LimitsType limits;
std::set<Move> searchMoves;
- int time[] = { 0, 0 }, inc[] = { 0, 0 };
+ string token;
while (is >> token)
{
- if (token == "infinite")
- limits.infinite = true;
- else if (token == "ponder")
- limits.ponder = true;
- else if (token == "wtime")
- is >> time[WHITE];
+ if (token == "wtime")
+ is >> limits.times[WHITE];
else if (token == "btime")
- is >> time[BLACK];
+ is >> limits.times[BLACK];
else if (token == "winc")
- is >> inc[WHITE];
+ is >> limits.incs[WHITE];
else if (token == "binc")
- is >> inc[BLACK];
+ is >> limits.incs[BLACK];
else if (token == "movestogo")
- is >> limits.movesToGo;
+ is >> limits.movestogo;
else if (token == "depth")
- is >> limits.maxDepth;
+ is >> limits.depth;
else if (token == "nodes")
- is >> limits.maxNodes;
+ is >> limits.nodes;
else if (token == "movetime")
- is >> limits.maxTime;
+ is >> limits.movetime;
+ else if (token == "infinite")
+ limits.infinite = true;
+ else if (token == "ponder")
+ limits.ponder = true;
else if (token == "searchmoves")
while (is >> token)
searchMoves.insert(move_from_uci(pos, token));
}
- limits.time = time[pos.side_to_move()];
- limits.increment = inc[pos.side_to_move()];
-
- Threads.start_thinking(pos, limits, searchMoves, true);
+ Threads.start_searching(pos, limits, searchMoves);
}