X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=d711d743a7a455a5db265715c62facc1f301ddeb;hp=b6fc06979599704a6bc67acbc367bb3ef0815b9f;hb=5e905800887da94d481978d3bddd9539b2111458;hpb=fc3ea7365ad95ec1da05ac559e4e659cf8a7f0f9 diff --git a/src/search.cpp b/src/search.cpp index b6fc0697..d711d743 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -28,7 +28,6 @@ #include "book.h" #include "evaluate.h" #include "history.h" -#include "misc.h" #include "movegen.h" #include "movepick.h" #include "search.h" @@ -43,6 +42,7 @@ namespace Search { LimitsType Limits; std::vector RootMoves; Position RootPosition; + Time SearchTime; } using std::string; @@ -67,7 +67,7 @@ namespace { const Depth RazorDepth = 4 * ONE_PLY; // Dynamic razoring margin based on depth - inline Value razor_margin(Depth d) { return Value(0x200 + 0x10 * int(d)); } + inline Value razor_margin(Depth d) { return Value(512 + 16 * int(d)); } // Maximum depth for use of dynamic threat detection when null move fails low const Depth ThreatDepth = 5 * ONE_PLY; @@ -77,13 +77,13 @@ namespace { // At Non-PV nodes we do an internal iterative deepening search // when the static evaluation is bigger then beta - IIDMargin. - const Value IIDMargin = Value(0x100); + const Value IIDMargin = Value(256); // Minimum depth for use of singular extension const Depth SingularExtensionDepth[] = { 8 * ONE_PLY, 6 * ONE_PLY }; // Futility margin for quiescence search - const Value FutilityMarginQS = Value(0x80); + const Value FutilityMarginQS = Value(128); // Futility lookup tables (initialized at startup) and their access functions Value FutilityMargins[16][64]; // [depth][moveNumber] @@ -118,7 +118,6 @@ namespace { size_t MultiPV, UCIMultiPV, PVIdx; TimeManager TimeMgr; - Time SearchTime; int BestMoveChanges; int SkillLevel; bool SkillLevelEnabled, Chess960; @@ -136,9 +135,9 @@ namespace { bool connected_moves(const Position& pos, Move m1, Move m2); Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply); - bool can_return_tt(const TTEntry* tte, Depth depth, Value beta, int ply); + bool can_return_tt(const TTEntry* tte, Depth depth, Value ttValue, Value beta); bool connected_threat(const Position& pos, Move m, Move threat); - Value refine_eval(const TTEntry* tte, Value defaultEval, int ply); + Value refine_eval(const TTEntry* tte, Value ttValue, Value defaultEval); Move do_skill_level(); string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE); void pv_info_to_log(Position& pos, int depth, Value score, int time, Move pv[]); @@ -177,11 +176,11 @@ namespace { } // Test for a capture that triggers a pawn endgame - if ( captureOrPromotion - && type_of(pos.piece_on(to_sq(m))) != PAWN + if ( captureOrPromotion + && type_of(pos.piece_on(to_sq(m))) != PAWN + && !is_special(m) && ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) - - PieceValueMidgame[pos.piece_on(to_sq(m))] == VALUE_ZERO) - && !is_special(m)) + - PieceValueMidgame[pos.piece_on(to_sq(m))] == VALUE_ZERO)) return true; return false; @@ -253,8 +252,7 @@ void Search::think() { Position& pos = RootPosition; 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(); @@ -267,7 +265,7 @@ void Search::think() { goto finalize; } - if (Options["OwnBook"]) + if (Options["OwnBook"] && !Limits.infinite) { Move bookMove = book.probe(pos, Options["Book File"], Options["Best Book Move"]); @@ -292,13 +290,13 @@ void Search::think() { log << "\nSearching: " << pos.to_fen() << "\ninfinite: " << Limits.infinite << " ponder: " << Limits.ponder - << " time: " << Limits.time - << " increment: " << Limits.increment - << " moves to go: " << Limits.movesToGo + << " time: " << Limits.time[pos.side_to_move()] + << " increment: " << Limits.inc[pos.side_to_move()] + << " moves to go: " << Limits.movestogo << endl; } - Threads.set_size(Options["Threads"]); + Threads.wake_up(); // Set best timer interval to avoid lagging under time pressure. Timer is // used to check for remaining available thinking time. @@ -310,9 +308,8 @@ void Search::think() { // We're ready to start searching. Call the iterative deepening loop function id_loop(pos); - // Stop timer and send all the slaves to sleep, if not already sleeping - Threads.set_timer(0); - Threads.set_size(1); + Threads.set_timer(0); // Stop timer + Threads.sleep(); if (Options["Use Search Log"]) { @@ -335,7 +332,7 @@ finalize: // 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[pos.thread()].wait_for_stop_or_ponderhit(); + pos.this_thread()->wait_for_stop_or_ponderhit(); // Best move could be MOVE_NONE when searching on a stalemate position cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], Chess960) @@ -363,7 +360,7 @@ namespace { 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. @@ -533,21 +530,20 @@ namespace { 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]; + Move movesSearched[64]; StateInfo st; const TTEntry *tte; Key posKey; Move ttMove, move, excludedMove, bestMove, threatMove; Depth ext, newDepth; Bound bt; - Value bestValue, value, oldAlpha; + Value bestValue, value, oldAlpha, ttValue; Value refinedValue, nullValue, futilityBase, futilityValue; bool isPvMove, inCheck, singularExtensionNode, givesCheck; bool captureOrPromotion, dangerous, doFullDepthSearch; int moveCount = 0, playedMoveCount = 0; - Thread& thread = Threads[pos.thread()]; + Thread* thisThread = pos.this_thread(); SplitPoint* sp = NULL; refinedValue = bestValue = value = -VALUE_INFINITE; @@ -556,14 +552,15 @@ namespace { ss->ply = (ss-1)->ply + 1; // Used to send selDepth info to GUI - if (PvNode && thread.maxPly < ss->ply) - thread.maxPly = ss->ply; + if (PvNode && thisThread->maxPly < ss->ply) + thisThread->maxPly = ss->ply; // Step 1. Initialize node if (SpNode) { tte = NULL; ttMove = excludedMove = MOVE_NONE; + ttValue = VALUE_ZERO; sp = ss->sp; bestMove = sp->bestMove; threatMove = sp->threatMove; @@ -584,7 +581,7 @@ namespace { // 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 @@ -613,27 +610,27 @@ namespace { posKey = excludedMove ? pos.exclusion_key() : pos.key(); tte = TT.probe(posKey); ttMove = RootNode ? RootMoves[PVIdx].pv[0] : tte ? tte->move() : MOVE_NONE; + ttValue = tte ? value_from_tt(tte->value(), ss->ply) : VALUE_ZERO; // At PV nodes we check for exact scores, while at non-PV nodes we check for // 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() == BOUND_EXACT - : can_return_tt(tte, depth, beta, ss->ply))) + : can_return_tt(tte, depth, ttValue, beta))) { TT.refresh(tte); ss->currentMove = ttMove; // Can be MOVE_NONE - value = value_from_tt(tte->value(), ss->ply); - if ( value >= beta - && ttMove + if ( ttValue >= beta + && ttMove && !pos.is_capture_or_promotion(ttMove) - && ttMove != ss->killers[0]) + && ttMove != ss->killers[0]) { ss->killers[1] = ss->killers[0]; ss->killers[0] = ttMove; } - return value; + return ttValue; } // Step 5. Evaluate the position statically and update parent's gain statistics @@ -645,7 +642,7 @@ namespace { ss->eval = tte->static_value(); ss->evalMargin = tte->static_value_margin(); - refinedValue = refine_eval(tte, ss->eval, ss->ply); + refinedValue = refine_eval(tte, ttValue, ss->eval); } else { @@ -655,9 +652,9 @@ namespace { // Update gain for the parent non-capture move given the static position // evaluation before and after the move. - if ( (move = (ss-1)->currentMove) != MOVE_NULL - && (ss-1)->eval != VALUE_NONE - && ss->eval != VALUE_NONE + if ( (move = (ss-1)->currentMove) != MOVE_NULL + && (ss-1)->eval != VALUE_NONE + && ss->eval != VALUE_NONE && !pos.captured_piece_type() && !is_special(move)) { @@ -672,7 +669,7 @@ namespace { && refinedValue + razor_margin(depth) < beta && ttMove == MOVE_NONE && abs(beta) < VALUE_MATE_IN_MAX_PLY - && !pos.has_pawn_on_7th(pos.side_to_move())) + && !pos.pawn_on_7th(pos.side_to_move())) { Value rbeta = beta - razor_margin(depth); Value v = qsearch(pos, ss, rbeta-1, rbeta, DEPTH_ZERO); @@ -809,17 +806,17 @@ split_point_start: // At split points actual search starts from here futilityBase = ss->eval + ss->evalMargin; singularExtensionNode = !RootNode && !SpNode - && depth >= SingularExtensionDepth[PvNode] - && ttMove != MOVE_NONE + && depth >= SingularExtensionDepth[PvNode] + && ttMove != MOVE_NONE && !excludedMove // Recursive singular search is not allowed && (tte->type() & BOUND_LOWER) - && tte->depth() >= depth - 3 * ONE_PLY; + && tte->depth() >= depth - 3 * ONE_PLY; // Step 11. Loop through moves // Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs - while ( bestValue < beta + while ( bestValue < beta && (move = mp.next_move()) != MOVE_NONE - && !thread.cutoff_occurred() + && !thisThread->cutoff_occurred() && !Signals.stop) { assert(is_ok(move)); @@ -849,7 +846,7 @@ split_point_start: // At split points actual search starts from here { Signals.firstRootMove = (moveCount == 1); - if (pos.thread() == 0 && SearchTime.elapsed() > 2000) + if (thisThread == Threads.main_thread() && SearchTime.elapsed() > 2000) cout << "info depth " << depth / ONE_PLY << " currmove " << move_to_uci(move, Chess960) << " currmovenumber " << moveCount + PVIdx << endl; @@ -866,20 +863,18 @@ split_point_start: // At split points actual search starts from here ext = ONE_PLY; else if (givesCheck && pos.see_sign(move) >= 0) - ext = PvNode ? ONE_PLY : ONE_PLY / 2; + ext = ONE_PLY / 2; // Singular extension search. If all moves but one fail low on a search of // (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move // is singular and should be extended. To verify this we do a reduced search // on all the other moves but the ttMove, if result is lower than ttValue minus // a margin then we extend ttMove. - if ( singularExtensionNode + if ( singularExtensionNode && !ext - && move == ttMove - && pos.pl_move_is_legal(move, ci.pinned)) + && move == ttMove + && pos.pl_move_is_legal(move, ci.pinned)) { - Value ttValue = value_from_tt(tte->value(), ss->ply); - if (abs(ttValue) < VALUE_KNOWN_WIN) { Value rBeta = ttValue - int(depth); @@ -949,7 +944,7 @@ split_point_start: // At split points actual search starts from here } ss->currentMove = move; - if (!SpNode && !captureOrPromotion) + if (!SpNode && !captureOrPromotion && playedMoveCount < 64) movesSearched[playedMoveCount++] = move; // Step 14. Make the move @@ -957,7 +952,7 @@ split_point_start: // At split points actual search starts from here // Step 15. Reduced depth search (LMR). If the move fails high will be // re-searched at full depth. - if ( depth > 3 * ONE_PLY + if ( depth > 3 * ONE_PLY && !isPvMove && !captureOrPromotion && !dangerous @@ -1043,7 +1038,7 @@ split_point_start: // At split points actual search starts from here && value < beta) // We want always alpha < beta alpha = value; - if (SpNode && !thread.cutoff_occurred()) + if (SpNode && !thisThread->cutoff_occurred()) { sp->bestValue = value; sp->bestMove = move; @@ -1056,11 +1051,12 @@ split_point_start: // At split points actual search starts from here // Step 19. Check for split if ( !SpNode - && depth >= Threads.min_split_depth() - && bestValue < beta - && Threads.available_slave_exists(pos.thread()) + && depth >= Threads.min_split_depth() + && depth - reduction(depth, moveCount) >= Threads.min_split_depth() + && bestValue < beta + && Threads.available_slave_exists(thisThread) && !Signals.stop - && !thread.cutoff_occurred()) + && !thisThread->cutoff_occurred()) bestValue = Threads.split(pos, ss, alpha, beta, bestValue, &bestMove, depth, threatMove, moveCount, &mp, NT); } @@ -1084,7 +1080,7 @@ split_point_start: // At split points actual search starts from here // Step 21. Update tables // Update transposition table entry, killers and history - if (!SpNode && !Signals.stop && !thread.cutoff_occurred()) + if (!SpNode && !Signals.stop && !thisThread->cutoff_occurred()) { move = bestValue <= oldAlpha ? MOVE_NONE : bestMove; bt = bestValue <= oldAlpha ? BOUND_UPPER @@ -1135,11 +1131,10 @@ split_point_start: // At split points actual search starts from here 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; Move ttMove, move, bestMove; - Value bestValue, value, evalMargin, futilityValue, futilityBase; + Value ttValue, bestValue, value, evalMargin, futilityValue, futilityBase; bool inCheck, enoughMaterial, givesCheck, evasionPrunable; const TTEntry* tte; Depth ttDepth; @@ -1163,11 +1158,12 @@ split_point_start: // At split points actual search starts from here // pruning, but only for move ordering. tte = TT.probe(pos.key()); ttMove = (tte ? tte->move() : MOVE_NONE); + ttValue = tte ? value_from_tt(tte->value(),ss->ply) : VALUE_ZERO; - if (!PvNode && tte && can_return_tt(tte, ttDepth, beta, ss->ply)) + if (!PvNode && tte && can_return_tt(tte, ttDepth, ttValue, beta)) { ss->currentMove = ttMove; // Can be MOVE_NONE - return value_from_tt(tte->value(), ss->ply); + return ttValue; } // Evaluate the position statically @@ -1250,8 +1246,8 @@ 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_MAX_PLY + && inCheck + && bestValue > VALUE_MATED_IN_MAX_PLY && !pos.is_capture(move) && !pos.can_castle(pos.side_to_move()); @@ -1342,7 +1338,7 @@ split_point_start: // At split points actual search starts from here // Rule 1. Checks which give opponent's king at most one escape square are dangerous b = kingAtt & ~pos.pieces(them) & ~newAtt & ~(1ULL << to); - if (single_bit(b)) // Catches also !b + if (!more_than_one(b)) return true; // Rule 2. Queen contact check is very dangerous @@ -1391,7 +1387,7 @@ split_point_start: // At split points actual search starts from here // Case 3: Moving through the vacated square p2 = pos.piece_on(f2); - if (piece_is_slider(p2) && (squares_between(f2, t2) & f1)) + if (piece_is_slider(p2) && (between_bb(f2, t2) & f1)) return true; // Case 4: The destination square for m2 is defended by the moving piece in m1 @@ -1402,7 +1398,7 @@ split_point_start: // At split points actual search starts from here // Case 5: Discovered check, checking piece is the piece moved in m1 ksq = pos.king_square(pos.side_to_move()); if ( piece_is_slider(p1) - && (squares_between(t1, ksq) & f2) + && (between_bb(t1, ksq) & f2) && (pos.attacks_from(p1, t1, pos.pieces() ^ f2) & ksq)) return true; @@ -1474,7 +1470,7 @@ split_point_start: // At split points actual search starts from here // Case 3: If the moving piece in the threatened move is a slider, don't // prune safe moves which block its ray. if ( piece_is_slider(pos.piece_on(tfrom)) - && (squares_between(tfrom, tto) & mto) + && (between_bb(tfrom, tto) & mto) && pos.see_sign(m) >= 0) return true; @@ -1485,9 +1481,7 @@ split_point_start: // At split points actual search starts from here // can_return_tt() returns true if a transposition table score can be used to // cut-off at a given point in search. - bool can_return_tt(const TTEntry* tte, Depth depth, Value beta, int ply) { - - Value v = value_from_tt(tte->value(), ply); + bool can_return_tt(const TTEntry* tte, Depth depth, Value v, Value beta) { return ( tte->depth() >= depth || v >= std::max(VALUE_MATE_IN_MAX_PLY, beta) @@ -1501,12 +1495,10 @@ split_point_start: // At split points actual search starts from here // refine_eval() returns the transposition table score if possible, otherwise // falls back on static position evaluation. - Value refine_eval(const TTEntry* tte, Value defaultEval, int ply) { + Value refine_eval(const TTEntry* tte, Value v, Value defaultEval) { assert(tte); - Value v = value_from_tt(tte->value(), ply); - if ( ((tte->type() & BOUND_LOWER) && v >= defaultEval) || ((tte->type() & BOUND_UPPER) && v < defaultEval)) return v; @@ -1834,8 +1826,7 @@ void Thread::idle_loop(SplitPoint* sp_master) { lock_release(Threads.splitLock); Stack ss[MAX_PLY_PLUS_2]; - Position pos(*sp->pos, threadID); - int master = sp->master; + Position pos(*sp->pos, this); memcpy(ss, sp->ss - 1, 4 * sizeof(Stack)); (ss+1)->sp = sp; @@ -1854,20 +1845,21 @@ void Thread::idle_loop(SplitPoint* sp_master) { assert(is_searching); is_searching = false; - sp->slavesMask &= ~(1ULL << threadID); + sp->slavesMask &= ~(1ULL << idx); sp->nodes += pos.nodes_searched(); - // After releasing the lock we cannot access anymore any SplitPoint - // related data in a reliably way becuase it could have been released - // under our feet by the sp master. - lock_release(sp->lock); - // 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 != master - && !Threads[master].is_searching) - Threads[master].wake_up(); + if ( Threads.use_sleeping_threads() + && this != sp->master + && !sp->master->is_searching) + sp->master->wake_up(); + + // After releasing the lock we cannot access anymore any SplitPoint + // related data in a safe way becuase it could have been released under + // our feet by the sp master. Also accessing other Thread objects is + // unsafe because if we are exiting there is a chance are already freed. + lock_release(sp->lock); } } } @@ -1899,6 +1891,6 @@ void check_time() { || stillAtFirstMove; if ( (Limits.use_time_management() && noMoreTime) - || (Limits.maxTime && e >= Limits.maxTime)) + || (Limits.movetime && e >= Limits.movetime)) Signals.stop = true; }