X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=f11388c595a162a1d77ddddb046d192bfd01945e;hp=cd4bf3e4d26faad6a5f3a3c7f5e535d4bab13e14;hb=50e094ef8db36ba1c1020b12594506f62532df17;hpb=c0136fb7286416be2ec179b4787f53d77023eeaf diff --git a/src/search.cpp b/src/search.cpp index cd4bf3e4..f11388c5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -301,9 +301,8 @@ namespace { bool connected_moves(const Position& pos, Move m1, Move m2); bool value_is_mate(Value value); bool move_is_killer(Move m, SearchStack* ss); - bool ok_to_do_nullmove(const Position& pos); - bool ok_to_prune(const Position& pos, Move m, Move threat); bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply); + bool connected_threat(const Position& pos, Move m, Move threat); Value refine_eval(const TTEntry* tte, Value defaultEval, int ply); void update_history(const Position& pos, Move move, Depth depth, Move movesSearched[], int moveCount); void update_killers(Move m, SearchStack* ss); @@ -1125,11 +1124,11 @@ namespace { // Step 6. Razoring (is omitted in PV nodes) if ( !PvNode + && depth < RazorDepth + && !isCheck && refinedValue < beta - razor_margin(depth) && ttMove == MOVE_NONE && (ss-1)->currentMove != MOVE_NULL - && depth < RazorDepth - && !isCheck && !value_is_mate(beta) && !pos.has_pawn_on_7th(pos.side_to_move())) { @@ -1147,10 +1146,10 @@ namespace { if ( !PvNode && allowNullmove && depth < RazorDepth + && refinedValue >= beta + futility_margin(depth, 0) && !isCheck && !value_is_mate(beta) - && ok_to_do_nullmove(pos) - && refinedValue >= beta + futility_margin(depth, 0)) + && pos.non_pawn_material(pos.side_to_move())) return refinedValue - futility_margin(depth, 0); // Step 8. Null move search with verification search (is omitted in PV nodes) @@ -1160,10 +1159,10 @@ namespace { if ( !PvNode && allowNullmove && depth > OnePly + && refinedValue >= beta - (depth >= 4 * OnePly ? NullMoveMargin : 0) && !isCheck && !value_is_mate(beta) - && ok_to_do_nullmove(pos) - && refinedValue >= beta - (depth >= 4 * OnePly ? NullMoveMargin : 0)) + && pos.non_pawn_material(pos.side_to_move())) { ss->currentMove = MOVE_NULL; @@ -1186,14 +1185,13 @@ namespace { if (nullValue >= value_mate_in(PLY_MAX)) nullValue = beta; - if (depth < 6 * OnePly) + // Do zugzwang verification search at high depths + if ( depth < 6 * OnePly + || search(pos, ss, alpha, beta, depth-5*OnePly, false, threadID) >= beta) return nullValue; - - // Do zugzwang verification search - Value v = search(pos, ss, alpha, beta, depth-5*OnePly, false, threadID); - if (v >= beta) - return nullValue; - } else { + } + else + { // The null move failed low, which means that we may be faced with // some kind of threat. If the previous move was reduced, check if // the move that refuted the null move was somehow connected to the @@ -1229,6 +1227,11 @@ namespace { // Initialize a MovePicker object for the current position MovePicker mp = MovePicker(pos, ttMove, depth, H, ss, (PvNode ? -VALUE_INFINITE : beta)); CheckInfo ci(pos); + bool singularExtensionNode = depth >= SingularExtensionDepth[PvNode] + && tte && tte->move() + && !excludedMove // Do not allow recursive singular extension search + && is_lower_bound(tte->type()) + && tte->depth() >= depth - 3 * OnePly; // Step 10. Loop through moves // Loop through all legal moves until no moves remain or a beta cutoff occurs @@ -1251,13 +1254,9 @@ namespace { // Singular extension search. We extend the TT move if its value is much better than // its siblings. To verify this we do a reduced search on all the other moves but the // ttMove, if result is lower then ttValue minus a margin then we extend ttMove. - if ( depth >= SingularExtensionDepth[PvNode] - && tte + if ( singularExtensionNode && move == tte->move() - && !excludedMove // Do not allow recursive singular extension search - && ext < OnePly - && is_lower_bound(tte->type()) - && tte->depth() >= depth - 3 * OnePly) + && ext < OnePly) { Value ttValue = value_from_tt(tte->value(), ply); @@ -1278,15 +1277,15 @@ namespace { // Step 12. Futility pruning (is omitted in PV nodes) if ( !PvNode + && !captureOrPromotion && !isCheck && !dangerous - && !captureOrPromotion - && !move_is_castle(move) - && move != ttMove) + && move != ttMove + && !move_is_castle(move)) { // Move count based pruning if ( moveCount >= futility_move_count(depth) - && ok_to_prune(pos, move, ss->threatMove) + && !(ss->threatMove && connected_threat(pos, move, ss->threatMove)) && bestValue > value_mated_in(PLY_MAX)) continue; @@ -1320,17 +1319,17 @@ namespace { bool doFullDepthSearch = true; if ( depth >= 3 * OnePly - && !dangerous && !captureOrPromotion + && !dangerous && !move_is_castle(move) && !move_is_killer(move, ss)) { ss->reduction = reduction(depth, moveCount); if (ss->reduction) { - Depth r = newDepth - ss->reduction; - value = r < OnePly ? -qsearch(pos, ss+1, -(alpha+1), -alpha, Depth(0), threadID) - : - search(pos, ss+1, -(alpha+1), -alpha, r, true, threadID); + Depth d = newDepth - ss->reduction; + value = d < OnePly ? -qsearch(pos, ss+1, -(alpha+1), -alpha, Depth(0), threadID) + : - search(pos, ss+1, -(alpha+1), -alpha, d, true, threadID); doFullDepthSearch = (value > alpha); } @@ -1346,12 +1345,12 @@ namespace { value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth-ss->reduction, true, threadID); doFullDepthSearch = (value > alpha); } + ss->reduction = Depth(0); // Restore original reduction } // Step 15. Full depth search if (doFullDepthSearch) { - ss->reduction = Depth(0); value = newDepth < OnePly ? -qsearch(pos, ss+1, -(alpha+1), -alpha, Depth(0), threadID) : - search(pos, ss+1, -(alpha+1), -alpha, newDepth, true, threadID); @@ -1386,13 +1385,13 @@ namespace { } // Step 18. Check for split - if ( TM.active_threads() > 1 + if ( depth >= MinimumSplitDepth + && TM.active_threads() > 1 && bestValue < beta - && depth >= MinimumSplitDepth - && Iteration <= 99 && TM.available_thread_exists(threadID) && !AbortSearch - && !TM.thread_should_stop(threadID)) + && !TM.thread_should_stop(threadID) + && Iteration <= 99) TM.split(pos, ss, &alpha, beta, &bestValue, depth, mateThreat, &moveCount, &mp, threadID, PvNode); } @@ -1681,7 +1680,7 @@ namespace { { // Move count based pruning if ( moveCount >= futility_move_count(sp->depth) - && ok_to_prune(pos, move, ss->threatMove) + && !(ss->threatMove && connected_threat(pos, move, ss->threatMove)) && sp->bestValue > value_mated_in(PLY_MAX)) { lock_grab(&(sp->lock)); @@ -1964,38 +1963,19 @@ namespace { } - // ok_to_do_nullmove() looks at the current position and decides whether - // doing a 'null move' should be allowed. In order to avoid zugzwang - // problems, null moves are not allowed when the side to move has very - // little material left. Currently, the test is a bit too simple: Null - // moves are avoided only when the side to move has only pawns left. - // It's probably a good idea to avoid null moves in at least some more - // complicated endgames, e.g. KQ vs KR. FIXME - - bool ok_to_do_nullmove(const Position& pos) { - - return pos.non_pawn_material(pos.side_to_move()) != Value(0); - } - - - // ok_to_prune() tests whether it is safe to forward prune a move. Only - // non-tactical moves late in the move list close to the leaves are - // candidates for pruning. + // connected_threat() tests whether it is safe to forward prune a move or if + // is somehow coonected to the threat move returned by null search. - bool ok_to_prune(const Position& pos, Move m, Move threat) { + bool connected_threat(const Position& pos, Move m, Move threat) { assert(move_is_ok(m)); - assert(threat == MOVE_NONE || move_is_ok(threat)); + assert(threat && move_is_ok(threat)); assert(!pos.move_is_check(m)); assert(!pos.move_is_capture_or_promotion(m)); assert(!pos.move_is_passed_pawn_push(m)); Square mfrom, mto, tfrom, tto; - // Prune if there isn't any threat move - if (threat == MOVE_NONE) - return true; - mfrom = move_from(m); mto = move_to(m); tfrom = move_from(threat); @@ -2003,7 +1983,7 @@ namespace { // Case 1: Don't prune moves which move the threatened piece if (mfrom == tto) - return false; + return true; // Case 2: If the threatened piece has value less than or equal to the // value of the threatening piece, don't prune move which defend it. @@ -2011,16 +1991,16 @@ namespace { && ( pos.midgame_value_of_piece_on(tfrom) >= pos.midgame_value_of_piece_on(tto) || pos.type_of_piece_on(tfrom) == KING) && pos.move_attacks_square(m, tto)) - return false; + return true; // 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)) && bit_is_set(squares_between(tfrom, tto), mto) && pos.see_sign(m) >= 0) - return false; + return true; - return true; + return false; }