X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=36bd0869301327d5cba8130e20ff824f4eeb246c;hp=9393db6505f106db0eb7b57ecc034444cefa34df;hb=b76c04c0975326d3274d7d7fb6df4edef7a040b5;hpb=939b621e5c67e9ef89b409fd773d38058341906c diff --git a/src/search.cpp b/src/search.cpp index 9393db65..36bd0869 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1,7 +1,7 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,11 +20,10 @@ #include #include #include -#include #include #include +#include #include -#include #include "book.h" #include "evaluate.h" @@ -42,11 +41,13 @@ namespace Search { volatile SignalsType Signals; LimitsType Limits; - std::vector SearchMoves; + std::vector RootMoves; Position RootPosition; } using std::string; +using std::cout; +using std::endl; using namespace Search; namespace { @@ -57,35 +58,6 @@ namespace { // Different node types, used as template parameter enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV }; - // RootMove struct is used for moves at the root of the tree. For each root - // move we store a score, a node count, and a PV (really a refutation in the - // case of moves which fail low). Score is normally set at -VALUE_INFINITE for - // all non-pv moves. - struct RootMove { - - RootMove(){} - RootMove(Move m) { - nodes = 0; - score = prevScore = -VALUE_INFINITE; - pv.push_back(m); - pv.push_back(MOVE_NONE); - } - - bool operator<(const RootMove& m) const { return score < m.score; } - bool operator==(const Move& m) const { return pv[0] == m; } - - void extract_pv_from_tt(Position& pos); - void insert_pv_in_tt(Position& pos); - - int64_t nodes; - Value score; - Value prevScore; - std::vector pv; - }; - - - /// Constants - // Lookup table to check if a Piece is a slider and its access function const bool Slidings[18] = { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1 }; inline bool piece_is_slider(Piece p) { return Slidings[p]; } @@ -135,14 +107,14 @@ namespace { return (Depth) Reductions[PvNode][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)]; } - // Easy move margin. An easy move candidate must be at least this much - // better than the second best move. + // Easy move margin. An easy move candidate must be at least this much better + // than the second best move. const Value EasyMoveMargin = Value(0x150); + // This is the minimum interval in msec between two check_time() calls + const int TimerResolution = 5; - /// Namespace variables - std::vector RootMoves; size_t MultiPV, UCIMultiPV, PVIdx; TimeManager TimeMgr; int BestMoveChanges; @@ -151,8 +123,6 @@ namespace { History H; - /// Local functions - template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); @@ -188,7 +158,7 @@ namespace { MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, Stack* ss, Value b) : MovePicker(p, ttm, d, h, ss, b), mp(ss->sp->mp) {} - Move get_next_move() { return mp->get_next_move(); } + Move next_move() { return mp->next_move(); } MovePicker* mp; }; @@ -197,19 +167,19 @@ namespace { FORCE_INLINE bool is_dangerous(const Position& pos, Move m, bool captureOrPromotion) { // Test for a pawn pushed to 7th or a passed pawn move - if (type_of(pos.piece_on(move_from(m))) == PAWN) + if (type_of(pos.piece_moved(m)) == PAWN) { Color c = pos.side_to_move(); - if ( relative_rank(c, move_to(m)) == RANK_7 - || pos.pawn_is_passed(c, move_to(m))) + if ( relative_rank(c, to_sq(m)) == RANK_7 + || pos.pawn_is_passed(c, to_sq(m))) return true; } // Test for a capture that triggers a pawn endgame if ( captureOrPromotion - && type_of(pos.piece_on(move_to(m))) != PAWN + && type_of(pos.piece_on(to_sq(m))) != PAWN && ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) - - PieceValueMidgame[pos.piece_on(move_to(m))] == VALUE_ZERO) + - PieceValueMidgame[pos.piece_on(to_sq(m))] == VALUE_ZERO) && !is_special(m)) return true; @@ -252,22 +222,22 @@ void Search::init() { int64_t Search::perft(Position& pos, Depth depth) { StateInfo st; - int64_t sum = 0; + int64_t cnt = 0; MoveList ml(pos); // At the last ply just return the number of moves (leaf nodes) - if (depth <= ONE_PLY) + if (depth == ONE_PLY) return ml.size(); CheckInfo ci(pos); for ( ; !ml.end(); ++ml) { pos.do_move(ml.move(), st, ci, pos.move_gives_check(ml.move(), ci)); - sum += perft(pos, depth - ONE_PLY); + cnt += perft(pos, depth - ONE_PLY); pos.undo_move(ml.move()); } - return sum; + return cnt; } @@ -285,27 +255,24 @@ void Search::think() { TimeMgr.init(Limits, pos.startpos_ply_counter()); TT.new_search(); H.clear(); - RootMoves.clear(); - - // Populate RootMoves with all the legal moves (default) or, if a SearchMoves - // is given, with the subset of legal moves to search. - for (MoveList ml(pos); !ml.end(); ++ml) - if ( SearchMoves.empty() - || count(SearchMoves.begin(), SearchMoves.end(), ml.move())) - RootMoves.push_back(RootMove(ml.move())); - if (Options["OwnBook"].value()) + if (RootMoves.empty()) { - if (Options["Book File"].value() != book.name()) - book.open(Options["Book File"].value()); + cout << "info depth 0 score " + << score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << endl; - Move bookMove = book.probe(pos, Options["Best Book Move"].value()); + RootMoves.push_back(MOVE_NONE); + goto finalize; + } + + if (Options["OwnBook"]) + { + Move bookMove = book.probe(pos, Options["Book File"], Options["Best Book Move"]); - if ( bookMove != MOVE_NONE - && count(RootMoves.begin(), RootMoves.end(), bookMove)) + if (bookMove && count(RootMoves.begin(), RootMoves.end(), bookMove)) { std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bookMove)); - goto finish; + goto finalize; } } @@ -313,31 +280,31 @@ void Search::think() { read_evaluation_uci_options(pos.side_to_move()); Threads.read_uci_options(); - TT.set_size(Options["Hash"].value()); - if (Options["Clear Hash"].value()) + TT.set_size(Options["Hash"]); + if (Options["Clear Hash"]) { - Options["Clear Hash"].set_value("false"); + Options["Clear Hash"] = false; TT.clear(); } - UCIMultiPV = Options["MultiPV"].value(); - SkillLevel = Options["Skill Level"].value(); + UCIMultiPV = Options["MultiPV"]; + SkillLevel = Options["Skill Level"]; // 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, (size_t)4) : UCIMultiPV); - if (Options["Use Search Log"].value()) + if (Options["Use Search Log"]) { - Log log(Options["Search Log Filename"].value()); + Log log(Options["Search Log Filename"]); log << "\nSearching: " << pos.to_fen() << "\ninfinite: " << Limits.infinite << " ponder: " << Limits.ponder << " time: " << Limits.time << " increment: " << Limits.increment << " moves to go: " << Limits.movesToGo - << std::endl; + << endl; } for (int i = 0; i < Threads.size(); i++) @@ -348,8 +315,8 @@ void Search::think() { // Set best timer interval to avoid lagging under time pressure. Timer is // used to check for remaining available thinking time. - if (TimeMgr.available_time()) - Threads.set_timer(std::min(100, std::max(TimeMgr.available_time() / 8, 20))); + if (Limits.use_time_management()) + Threads.set_timer(std::min(100, std::max(TimeMgr.available_time() / 16, TimerResolution))); else Threads.set_timer(100); @@ -360,33 +327,32 @@ void Search::think() { Threads.set_timer(0); Threads.set_size(1); - if (Options["Use Search Log"].value()) + if (Options["Use Search Log"]) { int e = elapsed_time(); - Log log(Options["Search Log Filename"].value()); + Log log(Options["Search Log Filename"]); log << "Nodes: " << pos.nodes_searched() << "\nNodes/second: " << (e > 0 ? pos.nodes_searched() * 1000 / e : 0) << "\nBest move: " << move_to_san(pos, RootMoves[0].pv[0]); StateInfo st; pos.do_move(RootMoves[0].pv[0], st); - log << "\nPonder move: " << move_to_san(pos, RootMoves[0].pv[1]) << std::endl; + log << "\nPonder move: " << move_to_san(pos, RootMoves[0].pv[1]) << endl; pos.undo_move(RootMoves[0].pv[0]); } -finish: +finalize: - // When we reach max depth we arrive here even without a StopRequest, but if - // we are pondering or in infinite search, we shouldn't print the best move - // before we are told to do so. + // When we reach max depth we arrive here even without Signals.stop is raised, + // but if we are pondering or in infinite search, we shouldn't print the best + // move before we are told to do so. if (!Signals.stop && (Limits.ponder || Limits.infinite)) Threads.wait_for_stop_or_ponderhit(); // Best move could be MOVE_NONE when searching on a stalemate position - printf("bestmove %s ponder %s\n", - move_to_uci(RootMoves[0].pv[0], Chess960).c_str(), - move_to_uci(RootMoves[0].pv[1], Chess960).c_str()); + cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], Chess960) + << " ponder " << move_to_uci(RootMoves[0].pv[1], Chess960) << endl; } @@ -398,7 +364,7 @@ namespace { void id_loop(Position& pos) { - Stack ss[PLY_MAX_PLUS_2]; + Stack ss[MAX_PLY_PLUS_2]; int depth, prevBestMoveChanges; Value bestValue, alpha, beta, delta; bool bestMoveNeverChanged = true; @@ -409,18 +375,8 @@ namespace { bestValue = delta = -VALUE_INFINITE; ss->currentMove = MOVE_NULL; // Hack to skip update gains - // Handle the special case of a mate/stalemate position - if (RootMoves.empty()) - { - printf("info depth 0%s\n", - score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW).c_str()); - - RootMoves.push_back(MOVE_NONE); - return; - } - // Iterative deepening loop until requested to stop or target depth reached - while (!Signals.stop && ++depth <= PLY_MAX && (!Limits.maxDepth || depth <= Limits.maxDepth)) + while (!Signals.stop && ++depth <= MAX_PLY && (!Limits.maxDepth || depth <= Limits.maxDepth)) { // 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. @@ -475,7 +431,7 @@ namespace { // If search has been stopped exit the aspiration window loop. // Sorting and writing PV back to TT is safe becuase RootMoves - // is still valid, although refers to previous iteration. + // is still valid, although refers to previous iteration. if (Signals.stop) break; @@ -511,7 +467,7 @@ namespace { if (SkillLevelEnabled && depth == 1 + SkillLevel) skillBest = do_skill_level(); - if (Options["Use Search Log"].value()) + if (Options["Use Search Log"]) pv_info_to_log(pos, depth, bestValue, elapsed_time(), &RootMoves[0].pv[0]); // Filter out startup noise when monitoring best move stability @@ -519,7 +475,7 @@ namespace { bestMoveNeverChanged = false; // Do we have time for the next iteration? Can we stop searching now? - if (!Signals.stop && !Signals.stopOnPonderhit && Limits.useTimeManagement()) + if (!Signals.stop && !Signals.stopOnPonderhit && Limits.use_time_management()) { bool stop = false; // Local variable, not the volatile Signals.stop @@ -534,15 +490,15 @@ namespace { stop = true; // Stop search early if one move seems to be much better than others - if ( depth >= 10 + if ( depth >= 12 && !stop - && ( bestMoveNeverChanged + && ( (bestMoveNeverChanged && pos.captured_piece_type()) || elapsed_time() > (TimeMgr.available_time() * 40) / 100)) { Value rBeta = bestValue - EasyMoveMargin; (ss+1)->excludedMove = RootMoves[0].pv[0]; (ss+1)->skipNullMove = true; - Value v = search(pos, ss+1, rBeta - 1, rBeta, (depth * ONE_PLY) / 2); + Value v = search(pos, ss+1, rBeta - 1, rBeta, (depth - 3) * ONE_PLY); (ss+1)->skipNullMove = false; (ss+1)->excludedMove = MOVE_NONE; @@ -587,19 +543,18 @@ namespace { const bool SpNode = (NT == SplitPointPV || NT == SplitPointNonPV || NT == SplitPointRoot); const bool RootNode = (NT == Root || NT == SplitPointRoot); - assert(alpha >= -VALUE_INFINITE && alpha <= VALUE_INFINITE); - assert(beta > alpha && beta <= VALUE_INFINITE); - assert(PvNode || alpha == beta - 1); + assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); + assert((alpha == beta - 1) || PvNode); + assert(depth > DEPTH_ZERO); assert(pos.thread() >= 0 && pos.thread() < Threads.size()); Move movesSearched[MAX_MOVES]; - int64_t nodes; StateInfo st; const TTEntry *tte; Key posKey; Move ttMove, move, excludedMove, threatMove; Depth ext, newDepth; - ValueType vt; + Bound bt; Value bestValue, value, oldAlpha; Value refinedValue, nullValue, futilityBase, futilityValue; bool isPvMove, inCheck, singularExtensionNode, givesCheck; @@ -618,32 +573,47 @@ namespace { thread.maxPly = ss->ply; // Step 1. Initialize node - if (!SpNode) - { - ss->currentMove = ss->bestMove = threatMove = (ss+1)->excludedMove = MOVE_NONE; - (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO; - (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; - } - else + if (SpNode) { - sp = ss->sp; tte = NULL; ttMove = excludedMove = MOVE_NONE; + sp = ss->sp; threatMove = sp->threatMove; + bestValue = sp->bestValue; + moveCount = sp->moveCount; // Lock must be held here + + assert(bestValue > -VALUE_INFINITE && moveCount > 0); + goto split_point_start; } + else + { + ss->currentMove = ss->bestMove = threatMove = (ss+1)->excludedMove = MOVE_NONE; + (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO; + (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; + + } // 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) + Signals.stop = true; + if (( Signals.stop || pos.is_draw() - || ss->ply > PLY_MAX) && !RootNode) + || ss->ply > MAX_PLY) && !RootNode) return VALUE_DRAW; - // Step 3. Mate distance pruning + // Step 3. Mate distance pruning. Even if we mate at the next move our score + // would be at best mate_in(ss->ply+1), but if alpha is already bigger because + // a shorter mate was found upward in the tree then there is no need to search + // further, we will never beat current alpha. Same logic but with reversed signs + // applies also in the opposite condition of being mated instead of giving mate, + // in this case return a fail-high score. if (!RootNode) { - alpha = std::max(value_mated_in(ss->ply), alpha); - beta = std::min(value_mate_in(ss->ply+1), beta); + alpha = std::max(mated_in(ss->ply), alpha); + beta = std::min(mate_in(ss->ply+1), beta); if (alpha >= beta) return alpha; } @@ -652,7 +622,7 @@ namespace { // We don't want the score of a partial search to overwrite a previous full search // TT value, so we use a different position key in case of an excluded move. excludedMove = ss->excludedMove; - posKey = excludedMove ? pos.get_exclusion_key() : pos.get_key(); + posKey = excludedMove ? pos.exclusion_key() : pos.key(); tte = TT.probe(posKey); ttMove = RootNode ? RootMoves[PVIdx].pv[0] : tte ? tte->move() : MOVE_NONE; @@ -660,7 +630,7 @@ namespace { // a fail high/low. Biggest advantage at probing at PV nodes is to have a // smooth experience in analysis mode. We don't probe at Root nodes otherwise // we should also update RootMoveList to avoid bogus output. - if (!RootNode && tte && (PvNode ? tte->depth() >= depth && tte->type() == VALUE_TYPE_EXACT + if (!RootNode && tte && (PvNode ? tte->depth() >= depth && tte->type() == BOUND_EXACT : can_return_tt(tte, depth, beta, ss->ply))) { TT.refresh(tte); @@ -692,7 +662,7 @@ namespace { else { refinedValue = ss->eval = evaluate(pos, ss->evalMargin); - TT.store(posKey, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, MOVE_NONE, ss->eval, ss->evalMargin); + TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, ss->eval, ss->evalMargin); } // Update gain for the parent non-capture move given the static position @@ -700,10 +670,10 @@ namespace { if ( (move = (ss-1)->currentMove) != MOVE_NULL && (ss-1)->eval != VALUE_NONE && ss->eval != VALUE_NONE - && pos.captured_piece_type() == PIECE_TYPE_NONE + && !pos.captured_piece_type() && !is_special(move)) { - Square to = move_to(move); + Square to = to_sq(move); H.update_gain(pos.piece_on(to), to, -(ss-1)->eval - ss->eval); } @@ -713,7 +683,7 @@ namespace { && !inCheck && refinedValue + razor_margin(depth) < beta && ttMove == MOVE_NONE - && abs(beta) < VALUE_MATE_IN_PLY_MAX + && abs(beta) < VALUE_MATE_IN_MAX_PLY && !pos.has_pawn_on_7th(pos.side_to_move())) { Value rbeta = beta - razor_margin(depth); @@ -732,7 +702,7 @@ namespace { && depth < RazorDepth && !inCheck && refinedValue - futility_margin(depth, 0) >= beta - && abs(beta) < VALUE_MATE_IN_PLY_MAX + && abs(beta) < VALUE_MATE_IN_MAX_PLY && pos.non_pawn_material(pos.side_to_move())) return refinedValue - futility_margin(depth, 0); @@ -742,7 +712,7 @@ namespace { && depth > ONE_PLY && !inCheck && refinedValue >= beta - && abs(beta) < VALUE_MATE_IN_PLY_MAX + && abs(beta) < VALUE_MATE_IN_MAX_PLY && pos.non_pawn_material(pos.side_to_move())) { ss->currentMove = MOVE_NULL; @@ -764,7 +734,7 @@ namespace { if (nullValue >= beta) { // Do not return unproven mate scores - if (nullValue >= VALUE_MATE_IN_PLY_MAX) + if (nullValue >= VALUE_MATE_IN_MAX_PLY) nullValue = beta; if (depth < 6 * ONE_PLY) @@ -805,19 +775,21 @@ namespace { && !inCheck && !ss->skipNullMove && excludedMove == MOVE_NONE - && abs(beta) < VALUE_MATE_IN_PLY_MAX) + && abs(beta) < VALUE_MATE_IN_MAX_PLY) { Value rbeta = beta + 200; Depth rdepth = depth - ONE_PLY - 3 * ONE_PLY; assert(rdepth >= ONE_PLY); + assert((ss-1)->currentMove != MOVE_NONE); MovePicker mp(pos, ttMove, H, pos.captured_piece_type()); CheckInfo ci(pos); - while ((move = mp.get_next_move()) != MOVE_NONE) + while ((move = mp.next_move()) != MOVE_NONE) if (pos.pl_move_is_legal(move, ci.pinned)) { + ss->currentMove = move; pos.do_move(move, st, ci, pos.move_gives_check(move, ci)); value = -search(pos, ss+1, -rbeta, -rbeta+1, rdepth); pos.undo_move(move); @@ -852,22 +824,15 @@ split_point_start: // At split points actual search starts from here && depth >= SingularExtensionDepth[PvNode] && ttMove != MOVE_NONE && !excludedMove // Recursive singular search is not allowed - && (tte->type() & VALUE_TYPE_LOWER) + && (tte->type() & BOUND_LOWER) && tte->depth() >= depth - 3 * ONE_PLY; - if (SpNode) - { - lock_grab(&(sp->lock)); - bestValue = sp->bestValue; - moveCount = sp->moveCount; - - assert(bestValue > -VALUE_INFINITE && moveCount > 0); - } // Step 11. Loop through moves // Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs while ( bestValue < beta - && (move = mp.get_next_move()) != MOVE_NONE - && !thread.cutoff_occurred()) + && (move = mp.next_move()) != MOVE_NONE + && !thread.cutoff_occurred() + && !Signals.stop) { assert(is_ok(move)); @@ -887,7 +852,7 @@ split_point_start: // At split points actual search starts from here if (SpNode) { moveCount = ++sp->moveCount; - lock_release(&(sp->lock)); + lock_release(sp->lock); } else moveCount++; @@ -895,11 +860,11 @@ split_point_start: // At split points actual search starts from here if (RootNode) { Signals.firstRootMove = (moveCount == 1); - nodes = pos.nodes_searched(); if (pos.thread() == 0 && elapsed_time() > 2000) - printf("info depth %i currmove %s currmovenumber %i\n", depth / ONE_PLY, - move_to_uci(move, Chess960).c_str(), moveCount + PVIdx); + cout << "info depth " << depth / ONE_PLY + << " currmove " << move_to_uci(move, Chess960) + << " currmovenumber " << moveCount + PVIdx << endl; } isPvMove = (PvNode && moveCount <= 1); @@ -951,14 +916,14 @@ split_point_start: // At split points actual search starts from here && !dangerous && move != ttMove && !is_castle(move) - && (bestValue > VALUE_MATED_IN_PLY_MAX || bestValue == -VALUE_INFINITE)) + && (bestValue > VALUE_MATED_IN_MAX_PLY || bestValue == -VALUE_INFINITE)) { // Move count based pruning if ( moveCount >= futility_move_count(depth) && (!threatMove || !connected_threat(pos, move, threatMove))) { if (SpNode) - lock_grab(&(sp->lock)); + lock_grab(sp->lock); continue; } @@ -968,12 +933,12 @@ split_point_start: // At split points actual search starts from here // but fixing this made program slightly weaker. Depth predictedDepth = newDepth - reduction(depth, moveCount); futilityValue = futilityBase + futility_margin(predictedDepth, moveCount) - + H.gain(pos.piece_on(move_from(move)), move_to(move)); + + H.gain(pos.piece_moved(move), to_sq(move)); if (futilityValue < beta) { if (SpNode) - lock_grab(&(sp->lock)); + lock_grab(sp->lock); continue; } @@ -983,7 +948,7 @@ split_point_start: // At split points actual search starts from here && pos.see_sign(move) < 0) { if (SpNode) - lock_grab(&(sp->lock)); + lock_grab(sp->lock); continue; } @@ -1014,11 +979,10 @@ split_point_start: // At split points actual search starts from here && ss->killers[1] != move) { ss->reduction = reduction(depth, moveCount); - Depth d = newDepth - ss->reduction; + Depth d = std::max(newDepth - ss->reduction, ONE_PLY); alpha = SpNode ? sp->alpha : alpha; - value = d < ONE_PLY ? -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) - : - search(pos, ss+1, -(alpha+1), -alpha, d); + value = -search(pos, ss+1, -(alpha+1), -alpha, d); doFullDepthSearch = (value > alpha && ss->reduction != DEPTH_ZERO); ss->reduction = DEPTH_ZERO; @@ -1049,19 +1013,18 @@ split_point_start: // At split points actual search starts from here // Step 18. Check for new best move if (SpNode) { - lock_grab(&(sp->lock)); + lock_grab(sp->lock); bestValue = sp->bestValue; alpha = sp->alpha; } - // Finished searching the move. If StopRequest is true, the search + // Finished searching the move. If Signals.stop is true, the search // was aborted because the user interrupted the search or because we // ran out of time. In this case, the return value of the search cannot // be trusted, and we don't update the best move and/or PV. if (RootNode && !Signals.stop) { RootMove& rm = *find(RootMoves.begin(), RootMoves.end(), move); - rm.nodes += pos.nodes_searched() - nodes; // PV move or new best move ? if (isPvMove || value > alpha) @@ -1116,11 +1079,11 @@ split_point_start: // At split points actual search starts from here // Step 20. Check for mate and stalemate // All legal moves have been searched and if there are no legal moves, it // must be mate or stalemate. Note that we can have a false positive in - // case of StopRequest or thread.cutoff_occurred() are set, but this is + // case of Signals.stop or thread.cutoff_occurred() are set, but this is // harmless because return value is discarded anyhow in the parent nodes. // If we are in a singular extension search then return a fail low score. if (!moveCount) - return excludedMove ? oldAlpha : inCheck ? value_mated_in(ss->ply) : VALUE_DRAW; + return excludedMove ? oldAlpha : inCheck ? mated_in(ss->ply) : VALUE_DRAW; // If we have pruned all the moves without searching return a fail-low score if (bestValue == -VALUE_INFINITE) @@ -1135,13 +1098,15 @@ split_point_start: // At split points actual search starts from here if (!SpNode && !Signals.stop && !thread.cutoff_occurred()) { move = bestValue <= oldAlpha ? MOVE_NONE : ss->bestMove; - vt = bestValue <= oldAlpha ? VALUE_TYPE_UPPER - : bestValue >= beta ? VALUE_TYPE_LOWER : VALUE_TYPE_EXACT; + bt = bestValue <= oldAlpha ? BOUND_UPPER + : bestValue >= beta ? BOUND_LOWER : BOUND_EXACT; - TT.store(posKey, value_to_tt(bestValue, ss->ply), vt, depth, move, ss->eval, ss->evalMargin); + TT.store(posKey, value_to_tt(bestValue, ss->ply), bt, depth, move, ss->eval, ss->evalMargin); // Update killers and history for non capture cut-off moves - if (bestValue >= beta && !pos.is_capture_or_promotion(move)) + if ( bestValue >= beta + && !pos.is_capture_or_promotion(move) + && !inCheck) { if (move != ss->killers[0]) { @@ -1151,25 +1116,17 @@ split_point_start: // At split points actual search starts from here // Increase history value of the cut-off move Value bonus = Value(int(depth) * int(depth)); - H.add(pos.piece_on(move_from(move)), move_to(move), bonus); + H.add(pos.piece_moved(move), to_sq(move), bonus); // Decrease history of all the other played non-capture moves for (int i = 0; i < playedMoveCount - 1; i++) { Move m = movesSearched[i]; - H.add(pos.piece_on(move_from(m)), move_to(m), -bonus); + H.add(pos.piece_moved(m), to_sq(m), -bonus); } } } - if (SpNode) - { - // Here we have the lock still grabbed - sp->is_slave[pos.thread()] = false; - sp->nodes += pos.nodes_searched(); - lock_release(&(sp->lock)); - } - assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); return bestValue; @@ -1186,10 +1143,9 @@ split_point_start: // At split points actual search starts from here const bool PvNode = (NT == PV); assert(NT == PV || NT == NonPV); - assert(alpha >= -VALUE_INFINITE && alpha <= VALUE_INFINITE); - assert(beta >= -VALUE_INFINITE && beta <= VALUE_INFINITE); - assert(PvNode || alpha == beta - 1); - assert(depth <= 0); + assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); + assert((alpha == beta - 1) || PvNode); + assert(depth <= DEPTH_ZERO); assert(pos.thread() >= 0 && pos.thread() < Threads.size()); StateInfo st; @@ -1198,14 +1154,14 @@ split_point_start: // At split points actual search starts from here bool inCheck, enoughMaterial, givesCheck, evasionPrunable; const TTEntry* tte; Depth ttDepth; - ValueType vt; + Bound bt; Value oldAlpha = alpha; ss->bestMove = ss->currentMove = MOVE_NONE; ss->ply = (ss-1)->ply + 1; // Check for an instant draw or maximum ply reached - if (pos.is_draw() || ss->ply > PLY_MAX) + if (pos.is_draw() || ss->ply > MAX_PLY) return VALUE_DRAW; // Decide whether or not to include checks, this fixes also the type of @@ -1216,7 +1172,7 @@ split_point_start: // At split points actual search starts from here // Transposition table lookup. At PV nodes, we don't use the TT for // pruning, but only for move ordering. - tte = TT.probe(pos.get_key()); + tte = TT.probe(pos.key()); ttMove = (tte ? tte->move() : MOVE_NONE); if (!PvNode && tte && can_return_tt(tte, ttDepth, beta, ss->ply)) @@ -1248,7 +1204,7 @@ split_point_start: // At split points actual search starts from here if (bestValue >= beta) { if (!tte) - TT.store(pos.get_key(), value_to_tt(bestValue, ss->ply), VALUE_TYPE_LOWER, DEPTH_NONE, MOVE_NONE, ss->eval, evalMargin); + TT.store(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER, DEPTH_NONE, MOVE_NONE, ss->eval, evalMargin); return bestValue; } @@ -1264,12 +1220,12 @@ split_point_start: // At split points actual search starts from here // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will // be generated. - MovePicker mp(pos, ttMove, depth, H, move_to((ss-1)->currentMove)); + MovePicker mp(pos, ttMove, depth, H, to_sq((ss-1)->currentMove)); CheckInfo ci(pos); // Loop through the moves until no moves remain or a beta cutoff occurs while ( bestValue < beta - && (move = mp.get_next_move()) != MOVE_NONE) + && (move = mp.next_move()) != MOVE_NONE) { assert(is_ok(move)); @@ -1285,7 +1241,7 @@ split_point_start: // At split points actual search starts from here && !pos.is_passed_pawn_push(move)) { futilityValue = futilityBase - + PieceValueEndgame[pos.piece_on(move_to(move))] + + PieceValueEndgame[pos.piece_on(to_sq(move))] + (is_enpassant(move) ? PawnValueEndgame : VALUE_ZERO); if (futilityValue < beta) @@ -1306,7 +1262,7 @@ split_point_start: // At split points actual search starts from here // Detect non-capture evasions that are candidate to be pruned evasionPrunable = !PvNode && inCheck - && bestValue > VALUE_MATED_IN_PLY_MAX + && bestValue > VALUE_MATED_IN_MAX_PLY && !pos.is_capture(move) && !pos.can_castle(pos.side_to_move()); @@ -1362,14 +1318,14 @@ split_point_start: // At split points actual search starts from here // All legal moves have been searched. A special case: If we're in check // and no legal moves were found, it is checkmate. if (inCheck && bestValue == -VALUE_INFINITE) - return value_mated_in(ss->ply); + return mated_in(ss->ply); // Plies to mate from the root // Update transposition table move = bestValue <= oldAlpha ? MOVE_NONE : ss->bestMove; - vt = bestValue <= oldAlpha ? VALUE_TYPE_UPPER - : bestValue >= beta ? VALUE_TYPE_LOWER : VALUE_TYPE_EXACT; + bt = bestValue <= oldAlpha ? BOUND_UPPER + : bestValue >= beta ? BOUND_LOWER : BOUND_EXACT; - TT.store(pos.get_key(), value_to_tt(bestValue, ss->ply), vt, ttDepth, move, ss->eval, evalMargin); + TT.store(pos.key(), value_to_tt(bestValue, ss->ply), bt, ttDepth, move, ss->eval, evalMargin); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1389,9 +1345,9 @@ split_point_start: // At split points actual search starts from here Color them; Value futilityValue, bv = *bestValue; - from = move_from(move); - to = move_to(move); - them = flip(pos.side_to_move()); + from = from_sq(move); + to = to_sq(move); + them = ~pos.side_to_move(); ksq = pos.king_square(them); kingAtt = pos.attacks_from(ksq); pc = pos.piece_on(from); @@ -1450,14 +1406,14 @@ split_point_start: // At split points actual search starts from here assert(is_ok(m2)); // Case 1: The moving piece is the same in both moves - f2 = move_from(m2); - t1 = move_to(m1); + f2 = from_sq(m2); + t1 = to_sq(m1); if (f2 == t1) return true; // Case 2: The destination square for m2 was vacated by m1 - t2 = move_to(m2); - f1 = move_from(m1); + t2 = to_sq(m2); + f1 = from_sq(m1); if (t2 == f1) return true; @@ -1487,30 +1443,31 @@ split_point_start: // At split points actual search starts from here // value_to_tt() adjusts a mate score from "plies to mate from the root" to - // "plies to mate from the current ply". Non-mate scores are unchanged. + // "plies to mate from the current position". Non-mate scores are unchanged. // The function is called before storing a value to the transposition table. Value value_to_tt(Value v, int ply) { - if (v >= VALUE_MATE_IN_PLY_MAX) + if (v >= VALUE_MATE_IN_MAX_PLY) return v + ply; - if (v <= VALUE_MATED_IN_PLY_MAX) + if (v <= VALUE_MATED_IN_MAX_PLY) return v - ply; return v; } - // value_from_tt() is the inverse of value_to_tt(): It adjusts a mate score from - // the transposition table to a mate score corrected for the current ply. + // value_from_tt() is the inverse of value_to_tt(): It adjusts a mate score + // from the transposition table (where refers to the plies to mate/be mated + // from current position) to "plies to mate/be mated from the root". Value value_from_tt(Value v, int ply) { - if (v >= VALUE_MATE_IN_PLY_MAX) + if (v >= VALUE_MATE_IN_MAX_PLY) return v - ply; - if (v <= VALUE_MATED_IN_PLY_MAX) + if (v <= VALUE_MATED_IN_MAX_PLY) return v + ply; return v; @@ -1529,10 +1486,10 @@ split_point_start: // At split points actual search starts from here Square mfrom, mto, tfrom, tto; - mfrom = move_from(m); - mto = move_to(m); - tfrom = move_from(threat); - tto = move_to(threat); + mfrom = from_sq(m); + mto = to_sq(m); + tfrom = from_sq(threat); + tto = to_sq(threat); // Case 1: Don't prune moves which move the threatened piece if (mfrom == tto) @@ -1565,11 +1522,11 @@ split_point_start: // At split points actual search starts from here Value v = value_from_tt(tte->value(), ply); return ( tte->depth() >= depth - || v >= std::max(VALUE_MATE_IN_PLY_MAX, beta) - || v < std::min(VALUE_MATED_IN_PLY_MAX, beta)) + || v >= std::max(VALUE_MATE_IN_MAX_PLY, beta) + || v < std::min(VALUE_MATED_IN_MAX_PLY, beta)) - && ( ((tte->type() & VALUE_TYPE_LOWER) && v >= beta) - || ((tte->type() & VALUE_TYPE_UPPER) && v < beta)); + && ( ((tte->type() & BOUND_LOWER) && v >= beta) + || ((tte->type() & BOUND_UPPER) && v < beta)); } @@ -1582,8 +1539,8 @@ split_point_start: // At split points actual search starts from here Value v = value_from_tt(tte->value(), ply); - if ( ((tte->type() & VALUE_TYPE_LOWER) && v >= defaultEval) - || ((tte->type() & VALUE_TYPE_UPPER) && v < defaultEval)) + if ( ((tte->type() & BOUND_LOWER) && v >= defaultEval) + || ((tte->type() & BOUND_UPPER) && v < defaultEval)) return v; return defaultEval; @@ -1598,9 +1555,9 @@ split_point_start: // At split points actual search starts from here static int searchStartTime; if (reset) - searchStartTime = get_system_time(); + searchStartTime = system_time(); - return get_system_time() - searchStartTime; + return system_time() - searchStartTime; } @@ -1615,10 +1572,10 @@ split_point_start: // At split points actual search starts from here std::stringstream s; - if (abs(v) < VALUE_MATE - PLY_MAX * ONE_PLY) - s << " score cp " << int(v) * 100 / int(PawnValueMidgame); // Scale to centipawns + if (abs(v) < VALUE_MATE_IN_MAX_PLY) + s << "cp " << v * 100 / int(PawnValueMidgame); else - s << " score mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; + s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; s << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); @@ -1634,7 +1591,6 @@ split_point_start: // At split points actual search starts from here int t = elapsed_time(); int selDepth = 0; - std::stringstream s; for (int i = 0; i < Threads.size(); i++) if (Threads[i].maxPly > selDepth) @@ -1649,19 +1605,19 @@ split_point_start: // At split points actual search starts from here int d = (updated ? depth : depth - 1); Value v = (updated ? RootMoves[i].score : RootMoves[i].prevScore); - - s << "info depth " << d - << " seldepth " << selDepth - << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v)) - << " nodes " << pos.nodes_searched() - << " nps " << (t > 0 ? pos.nodes_searched() * 1000 / t : 0) - << " time " << t - << " multipv " << i + 1 << " pv"; + std::stringstream s; for (int j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++) s << " " << move_to_uci(RootMoves[i].pv[j], Chess960); - printf("%s\n", s.str().c_str()); // Much faster than std::cout + cout << "info depth " << d + << " seldepth " << selDepth + << " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v)) + << " nodes " << pos.nodes_searched() + << " nps " << (t > 0 ? pos.nodes_searched() * 1000 / t : 0) + << " time " << t + << " multipv " << i + 1 + << " pv" << s.str() << endl; } } @@ -1693,9 +1649,9 @@ split_point_start: // At split points actual search starts from here std::stringstream s; - if (v >= VALUE_MATE_IN_PLY_MAX) + if (v >= VALUE_MATE_IN_MAX_PLY) s << "#" << (VALUE_MATE - v + 1) / 2; - else if (v <= VALUE_MATED_IN_PLY_MAX) + else if (v <= VALUE_MATED_IN_MAX_PLY) s << "-#" << (VALUE_MATE + v) / 2; else s << std::setprecision(2) << std::fixed << std::showpos @@ -1709,7 +1665,7 @@ split_point_start: // At split points actual search starts from here const int64_t K = 1000; const int64_t M = 1000000; - StateInfo state[PLY_MAX_PLUS_2], *st = state; + StateInfo state[MAX_PLY_PLUS_2], *st = state; Move* m = pv; string san, padding; size_t length; @@ -1750,8 +1706,8 @@ split_point_start: // At split points actual search starts from here while (m != pv) pos.undo_move(*--m); - Log l(Options["Search Log Filename"].value()); - l << s.str() << std::endl; + Log l(Options["Search Log Filename"]); + l << s.str() << endl; } @@ -1765,7 +1721,7 @@ split_point_start: // At split points actual search starts from here static RKISS rk; // PRNG sequence should be not deterministic - for (int i = abs(get_system_time() % 50); i > 0; i--) + for (int i = abs(system_time() % 50); i > 0; i--) rk.rand(); // RootMoves are already sorted by score in descending order @@ -1799,105 +1755,105 @@ split_point_start: // At split points actual search starts from here return best; } +} // namespace - // 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 - // long PV to print that is important for position analysis. - void RootMove::extract_pv_from_tt(Position& pos) { +/// RootMove::extract_pv_from_tt() builds a PV by adding moves from the TT table. +/// We consider also failing high nodes and not only BOUND_EXACT nodes so to +/// allow to always have a ponder move even when we fail high at root, and a +/// long PV to print that is important for position analysis. - StateInfo state[PLY_MAX_PLUS_2], *st = state; - TTEntry* tte; - int ply = 1; - Move m = pv[0]; +void RootMove::extract_pv_from_tt(Position& pos) { - assert(m != MOVE_NONE && pos.is_pseudo_legal(m)); + StateInfo state[MAX_PLY_PLUS_2], *st = state; + TTEntry* tte; + int ply = 1; + Move m = pv[0]; - pv.clear(); - pv.push_back(m); - pos.do_move(m, *st++); + assert(m != MOVE_NONE && pos.is_pseudo_legal(m)); - while ( (tte = TT.probe(pos.get_key())) != NULL - && tte->move() != MOVE_NONE - && pos.is_pseudo_legal(tte->move()) - && pos.pl_move_is_legal(tte->move(), pos.pinned_pieces()) - && ply < PLY_MAX - && (!pos.is_draw() || ply < 2)) - { - pv.push_back(tte->move()); - pos.do_move(tte->move(), *st++); - ply++; - } - pv.push_back(MOVE_NONE); + pv.clear(); + pv.push_back(m); + pos.do_move(m, *st++); - do pos.undo_move(pv[--ply]); while (ply); + while ( (tte = TT.probe(pos.key())) != NULL + && tte->move() != MOVE_NONE + && pos.is_pseudo_legal(tte->move()) + && pos.pl_move_is_legal(tte->move(), pos.pinned_pieces()) + && ply < MAX_PLY + && (!pos.is_draw() || ply < 2)) + { + pv.push_back(tte->move()); + pos.do_move(tte->move(), *st++); + ply++; } + pv.push_back(MOVE_NONE); + 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. - void RootMove::insert_pv_in_tt(Position& pos) { +/// RootMove::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. - StateInfo state[PLY_MAX_PLUS_2], *st = state; - TTEntry* tte; - Key k; - Value v, m = VALUE_NONE; - int ply = 0; +void RootMove::insert_pv_in_tt(Position& pos) { - assert(pv[ply] != MOVE_NONE && pos.is_pseudo_legal(pv[ply])); + StateInfo state[MAX_PLY_PLUS_2], *st = state; + TTEntry* tte; + Key k; + Value v, m = VALUE_NONE; + int ply = 0; - do { - k = pos.get_key(); - tte = TT.probe(k); + assert(pv[ply] != MOVE_NONE && pos.is_pseudo_legal(pv[ply])); - // Don't overwrite existing correct entries - if (!tte || tte->move() != pv[ply]) - { - v = (pos.in_check() ? VALUE_NONE : evaluate(pos, m)); - TT.store(k, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, pv[ply], v, m); - } - pos.do_move(pv[ply], *st++); + do { + k = pos.key(); + tte = TT.probe(k); - } while (pv[++ply] != MOVE_NONE); + // Don't overwrite existing correct entries + if (!tte || tte->move() != pv[ply]) + { + v = (pos.in_check() ? VALUE_NONE : evaluate(pos, m)); + TT.store(k, VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[ply], v, m); + } + pos.do_move(pv[ply], *st++); - do pos.undo_move(pv[--ply]); while (ply); - } + } while (pv[++ply] != MOVE_NONE); -} // namespace + do pos.undo_move(pv[--ply]); while (ply); +} /// Thread::idle_loop() is where the thread is parked when it has no work to do. -/// The parameter 'sp', if non-NULL, is a pointer to an active SplitPoint object -/// for which the thread is the master. +/// The parameter 'master_sp', if non-NULL, is a pointer to an active SplitPoint +/// object for which the thread is the master. -void Thread::idle_loop(SplitPoint* sp) { +void Thread::idle_loop(SplitPoint* sp_master) { - while (true) + // 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. + while (!sp_master || sp_master->slavesMask) { // If we are not searching, wait for a condition to be signaled // instead of wasting CPU time polling for work. while ( do_sleep - || do_terminate - || (Threads.use_sleeping_threads() && !is_searching)) + || do_exit + || (!is_searching && Threads.use_sleeping_threads())) { - assert((!sp && threadID) || Threads.use_sleeping_threads()); - - if (do_terminate) + if (do_exit) { - assert(!sp); + assert(!sp_master); return; } // Grab the lock to avoid races with Thread::wake_up() - lock_grab(&sleepLock); + lock_grab(sleepLock); // If we are master and all slaves have finished don't go to sleep - if (sp && Threads.split_point_finished(sp)) + if (sp_master && !sp_master->slavesMask) { - lock_release(&sleepLock); + lock_release(sleepLock); break; } @@ -1906,74 +1862,68 @@ void Thread::idle_loop(SplitPoint* sp) { // in the meanwhile, allocated us and sent the wake_up() call before we // had the chance to grab the lock. if (do_sleep || !is_searching) - cond_wait(&sleepCond, &sleepLock); + cond_wait(sleepCond, sleepLock); - lock_release(&sleepLock); + lock_release(sleepLock); } // If this thread has been assigned work, launch a search if (is_searching) { - assert(!do_terminate); + assert(!do_sleep && !do_exit); // Copy split point position and search stack and call search() - Stack ss[PLY_MAX_PLUS_2]; - SplitPoint* tsp = splitPoint; - Position pos(*tsp->pos, threadID); - - memcpy(ss, tsp->ss - 1, 4 * sizeof(Stack)); - (ss+1)->sp = tsp; - - if (tsp->nodeType == Root) - search(pos, ss+1, tsp->alpha, tsp->beta, tsp->depth); - else if (tsp->nodeType == PV) - search(pos, ss+1, tsp->alpha, tsp->beta, tsp->depth); - else if (tsp->nodeType == NonPV) - search(pos, ss+1, tsp->alpha, tsp->beta, tsp->depth); + Stack ss[MAX_PLY_PLUS_2]; + SplitPoint* sp = splitPoint; + Position pos(*sp->pos, threadID); + + memcpy(ss, sp->ss - 1, 4 * sizeof(Stack)); + (ss+1)->sp = sp; + + lock_grab(sp->lock); + + if (sp->nodeType == Root) + search(pos, ss+1, sp->alpha, sp->beta, sp->depth); + else if (sp->nodeType == PV) + search(pos, ss+1, sp->alpha, sp->beta, sp->depth); + else if (sp->nodeType == NonPV) + search(pos, ss+1, sp->alpha, sp->beta, sp->depth); else assert(false); assert(is_searching); + // We return from search with lock held + sp->slavesMask &= ~(1ULL << threadID); + sp->nodes += pos.nodes_searched(); + lock_release(sp->lock); + is_searching = false; // 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 ( Threads.use_sleeping_threads() - && threadID != tsp->master - && !Threads[tsp->master].is_searching) - Threads[tsp->master].wake_up(); - } - - // 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 && Threads.split_point_finished(sp)) - { - // Because sp->is_slave[] is reset under lock protection, - // be sure sp->lock has been released before to return. - lock_grab(&(sp->lock)); - lock_release(&(sp->lock)); - return; + && threadID != sp->master + && !Threads[sp->master].is_searching) + Threads[sp->master].wake_up(); } } } -/// do_timer_event() is called by the timer thread when the timer triggers. It -/// is used to print debug info and, more important, to detect when we are out of +/// check_time() is called by the timer thread when the timer triggers. It is +/// used to print debug info and, more important, to detect when we are out of /// available time and so stop the search. -void do_timer_event() { +void check_time() { static int lastInfoTime; int e = elapsed_time(); - if (get_system_time() - lastInfoTime >= 1000 || !lastInfoTime) + if (system_time() - lastInfoTime >= 1000 || !lastInfoTime) { - lastInfoTime = get_system_time(); - - dbg_print_mean(); - dbg_print_hit_rate(); + lastInfoTime = system_time(); + dbg_print(); } if (Limits.ponder) @@ -1983,11 +1933,10 @@ void do_timer_event() { && !Signals.failedLowAtRoot && e > TimeMgr.available_time(); - bool noMoreTime = e > TimeMgr.maximum_time() + bool noMoreTime = e > TimeMgr.maximum_time() - 2 * TimerResolution || stillAtFirstMove; - if ( (Limits.useTimeManagement() && noMoreTime) - || (Limits.maxTime && e >= Limits.maxTime) - /* missing nodes limit */ ) // FIXME + if ( (Limits.use_time_management() && noMoreTime) + || (Limits.maxTime && e >= Limits.maxTime)) Signals.stop = true; }