X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=63c9e076f61ce6a713a162bace28dfde429b9fa9;hp=bba0cb6593c2944cf0dccf2e20a3f43ca1850fa7;hb=c2cefa6de0807e389709a1a12591eb3a86017d08;hpb=fff6b9f0614243a14c7ba6aa8c846d8d4025d3a6 diff --git a/src/search.cpp b/src/search.cpp index bba0cb65..63c9e076 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -84,7 +84,7 @@ namespace { size_t PVSize, PVIdx; TimeManager TimeMgr; - int BestMoveChanges; + float BestMoveChanges; Value DrawValue[COLOR_NB]; HistoryStats History; GainsStats Gains; @@ -99,7 +99,6 @@ namespace { void id_loop(Position& pos); Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply); - bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta); bool allows(const Position& pos, Move first, Move second); bool refutes(const Position& pos, Move first, Move second); string uci_pv(const Position& pos, int depth, Value alpha, Value beta); @@ -153,9 +152,8 @@ void Search::init() { // Init futility move count array for (d = 0; d < 32; d++) { - FutilityMoveCounts[1][d] = int(3.001 + 0.3 * pow(double(d), 1.8)); - FutilityMoveCounts[0][d] = d < 5 ? FutilityMoveCounts[1][d] - : 3 * FutilityMoveCounts[1][d] / 4; + FutilityMoveCounts[0][d] = int(3 + 0.3 * pow(double(d ), 1.8)) * 3/4 + (2 < d && d < 5); + FutilityMoveCounts[1][d] = int(3 + 0.3 * pow(double(d + 0.98), 1.8)); } } @@ -238,7 +236,7 @@ void Search::think() { } // Reset the threads, still sleeping: will be wake up at split time - for (size_t i = 0; i < Threads.size(); i++) + for (size_t i = 0; i < Threads.size(); ++i) Threads[i]->maxPly = 0; Threads.sleepWhileIdle = Options["Idle Threads Sleep"]; @@ -304,14 +302,15 @@ namespace { void id_loop(Position& pos) { - Stack stack[MAX_PLY_PLUS_3], *ss = stack+2; // To allow referencing (ss-2) - int depth, prevBestMoveChanges; + Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2) + int depth; Value bestValue, alpha, beta, delta; std::memset(ss-2, 0, 5 * sizeof(Stack)); (ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains - depth = BestMoveChanges = 0; + depth = 0; + BestMoveChanges = 0; bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; @@ -333,14 +332,14 @@ namespace { // Iterative deepening loop until requested to stop or target depth reached while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth)) { + // Age out PV variability metric + BestMoveChanges *= 0.8f; + // 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. - for (size_t i = 0; i < RootMoves.size(); i++) + for (size_t i = 0; i < RootMoves.size(); ++i) RootMoves[i].prevScore = RootMoves[i].score; - prevBestMoveChanges = BestMoveChanges; // Only sensible when PVSize == 1 - BestMoveChanges = 0; - // MultiPV loop. We perform a full root search for each PV line for (PVIdx = 0; PVIdx < PVSize; PVIdx++) { @@ -368,7 +367,7 @@ namespace { // Write PV back to transposition table in case the relevant // entries have been overwritten during the search. - for (size_t i = 0; i <= PVIdx; i++) + for (size_t i = 0; i <= PVIdx; ++i) RootMoves[i].insert_pv_in_tt(pos); // If search has been stopped return immediately. Sorting and @@ -438,7 +437,7 @@ namespace { // Take in account some extra time if the best move has changed if (depth > 4 && depth < 50 && PVSize == 1) - TimeMgr.pv_instability(BestMoveChanges, prevBestMoveChanges); + TimeMgr.pv_instability(BestMoveChanges); // Stop search if most of available time is already consumed. We // probably don't have enough time to search the first move at the @@ -664,7 +663,7 @@ namespace { // Step 8. Null move search with verification search (is omitted in PV nodes) if ( !PvNode && !ss->skipNullMove - && depth > ONE_PLY + && depth >= 2 * ONE_PLY && eval >= beta && abs(beta) < VALUE_MATE_IN_MAX_PLY && pos.non_pawn_material(pos.side_to_move())) @@ -775,10 +774,13 @@ moves_loop: // When in check and at SpNode search starts from here MovePicker mp(pos, ttMove, depth, History, countermoves, ss); CheckInfo ci(pos); value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc - improving = ss->staticEval >= (ss-2)->staticEval; + improving = ss->staticEval >= (ss-2)->staticEval + || ss->staticEval == VALUE_NONE + ||(ss-2)->staticEval == VALUE_NONE; + singularExtensionNode = !RootNode && !SpNode - && depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY) + && depth >= 8 * ONE_PLY && ttMove != MOVE_NONE && !excludedMove // Recursive singular search is not allowed && (tte->bound() & BOUND_LOWER) @@ -833,7 +835,7 @@ moves_loop: // When in check and at SpNode search starts from here ext = ONE_PLY; else if (givesCheck && pos.see_sign(move) >= 0) - ext = ONE_PLY / 2; + ext = inCheck || ss->staticEval <= alpha ? ONE_PLY : 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 @@ -935,10 +937,9 @@ moves_loop: // When in check and at SpNode 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 && !pvMove && !captureOrPromotion - && !dangerous && move != ttMove && move != ss->killers[0] && move != ss->killers[1]) @@ -948,8 +949,11 @@ moves_loop: // When in check and at SpNode search starts from here if (!PvNode && cutNode) ss->reduction += ONE_PLY; + else if (History[pos.piece_on(to_sq(move))][to_sq(move)] < 0) + ss->reduction += ONE_PLY / 2; + if (move == countermoves[0] || move == countermoves[1]) - ss->reduction = std::max(DEPTH_ZERO, ss->reduction-ONE_PLY); + ss->reduction = std::max(DEPTH_ZERO, ss->reduction - ONE_PLY); Depth d = std::max(newDepth - ss->reduction, ONE_PLY); if (SpNode) @@ -1101,7 +1105,7 @@ moves_loop: // When in check and at SpNode search starts from here // played non-capture moves. Value bonus = Value(int(depth) * int(depth)); History.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus); - for (int i = 0; i < quietCount - 1; i++) + for (int i = 0; i < quietCount - 1; ++i) { Move m = quietsSearched[i]; History.update(pos.piece_moved(m), to_sq(m), -bonus); @@ -1228,6 +1232,7 @@ moves_loop: // When in check and at SpNode search starts from here && !givesCheck && move != ttMove && type_of(move) != PROMOTION + && futilityBase > -VALUE_KNOWN_WIN && !pos.is_passed_pawn_push(move)) { futilityValue = futilityBase @@ -1264,16 +1269,6 @@ moves_loop: // When in check and at SpNode search starts from here && pos.see_sign(move) < 0) continue; - // Don't search useless checks - if ( !PvNode - && !InCheck - && givesCheck - && move != ttMove - && !pos.is_capture_or_promotion(move) - && ss->staticEval + PawnValueMg / 4 < beta - && !check_is_dangerous(pos, move, futilityBase, beta)) - continue; - // Check for legality only before to do the move if (!pos.pl_move_is_legal(move, ci.pinned)) continue; @@ -1351,42 +1346,6 @@ moves_loop: // When in check and at SpNode search starts from here } - // check_is_dangerous() tests if a checking move can be pruned in qsearch() - - bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta) - { - Piece pc = pos.piece_moved(move); - Square from = from_sq(move); - Square to = to_sq(move); - Color them = ~pos.side_to_move(); - Square ksq = pos.king_square(them); - Bitboard enemies = pos.pieces(them); - Bitboard kingAtt = pos.attacks_from(ksq); - Bitboard occ = pos.pieces() ^ from ^ ksq; - Bitboard oldAtt = pos.attacks_from(pc, from, occ); - Bitboard newAtt = pos.attacks_from(pc, to, occ); - - // Checks which give opponent's king at most one escape square are dangerous - if (!more_than_one(kingAtt & ~(enemies | newAtt | to))) - return true; - - // Queen contact check is very dangerous - if (type_of(pc) == QUEEN && (kingAtt & to)) - return true; - - // Creating new double threats with checks is dangerous - Bitboard b = (enemies ^ ksq) & newAtt & ~oldAtt; - while (b) - { - // Note that here we generate illegal "double move"! - if (futilityBase + PieceValue[EG][pos.piece_on(pop_lsb(&b))] >= beta) - return true; - } - - return false; - } - - // allows() tests whether the 'first' move at previous ply somehow makes the // 'second' move possible, for instance if the moving piece is the same in // both moves. Normally the second move is the threat (the best move returned @@ -1397,7 +1356,7 @@ moves_loop: // When in check and at SpNode search starts from here assert(is_ok(first)); assert(is_ok(second)); assert(color_of(pos.piece_on(from_sq(second))) == ~pos.side_to_move()); - assert(color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move()); + assert(type_of(first) == CASTLE || color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move()); Square m1from = from_sq(first); Square m2from = from_sq(second); @@ -1405,7 +1364,10 @@ moves_loop: // When in check and at SpNode search starts from here Square m2to = to_sq(second); // The piece is the same or second's destination was vacated by the first move - if (m1to == m2from || m2to == m1from) + // We exclude the trivial case where a sliding piece does in two moves what + // it could do in one move: eg. Ra1a2, Ra2a3. + if ( m2to == m1from + || (m1to == m2from && !squares_aligned(m1from, m2from, m2to))) return true; // Second one moves through the square vacated by first one @@ -1497,7 +1459,7 @@ moves_loop: // When in check and at SpNode search starts from here // Choose best move. For each move score we add two terms both dependent on // weakness, one deterministic and bigger for weaker moves, and one random, // then we choose the move with the resulting highest score. - for (size_t i = 0; i < PVSize; i++) + for (size_t i = 0; i < PVSize; ++i) { int s = RootMoves[i].score; @@ -1530,11 +1492,11 @@ moves_loop: // When in check and at SpNode search starts from here size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size()); int selDepth = 0; - for (size_t i = 0; i < Threads.size(); i++) + for (size_t i = 0; i < Threads.size(); ++i) if (Threads[i]->maxPly > selDepth) selDepth = Threads[i]->maxPly; - for (size_t i = 0; i < uciPVSize; i++) + for (size_t i = 0; i < uciPVSize; ++i) { bool updated = (i <= PVIdx); @@ -1573,7 +1535,7 @@ moves_loop: // When in check and at SpNode search starts from here void RootMove::extract_pv_from_tt(Position& pos) { - StateInfo state[MAX_PLY_PLUS_3], *st = state; + StateInfo state[MAX_PLY_PLUS_6], *st = state; const TTEntry* tte; int ply = 0; Move m = pv[0]; @@ -1606,7 +1568,7 @@ void RootMove::extract_pv_from_tt(Position& pos) { void RootMove::insert_pv_in_tt(Position& pos) { - StateInfo state[MAX_PLY_PLUS_3], *st = state; + StateInfo state[MAX_PLY_PLUS_6], *st = state; const TTEntry* tte; int ply = 0; @@ -1681,7 +1643,7 @@ void Thread::idle_loop() { Threads.mutex.unlock(); - Stack stack[MAX_PLY_PLUS_3], *ss = stack+2; // To allow referencing (ss-2) + Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2) Position pos(*sp->pos, this); std::memcpy(ss-2, sp->ss-2, 5 * sizeof(Stack)); @@ -1771,7 +1733,7 @@ void check_time() { // Loop across all split points and sum accumulated SplitPoint nodes plus // all the currently active positions nodes. - for (size_t i = 0; i < Threads.size(); i++) + for (size_t i = 0; i < Threads.size(); ++i) for (int j = 0; j < Threads[i]->splitPointsSize; j++) { SplitPoint& sp = Threads[i]->splitPoints[j];