X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fsearch.cpp;h=2eed74b87592596c369f7e29a6b521d10f97e29c;hb=29c1e072b669c2257e4b48094391e7dc39fb31a5;hp=7d618a65b56442e595fbf75dc9fc4cd9dd8ffedb;hpb=0827e00f10709a4475ece44f0588277fc8cdcd9d;p=stockfish diff --git a/src/search.cpp b/src/search.cpp index 7d618a65..2eed74b8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -615,22 +615,24 @@ namespace { if (!rootNode) (ss+2)->statScore = 0; - // Step 4. Transposition table lookup. 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. + // Step 4. Transposition table lookup. excludedMove = ss->excludedMove; - posKey = excludedMove == MOVE_NONE ? pos.key() : pos.key() ^ make_key(excludedMove); + posKey = pos.key(); tte = TT.probe(posKey, ss->ttHit); ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ss->ttHit ? tte->move() : MOVE_NONE; ttCapture = ttMove && pos.capture(ttMove); + + // At this point, if excluded, skip straight to step 6, static eval. However, + // to save indentation, we list the condition in all code between here and there. if (!excludedMove) ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit + && !excludedMove && tte->depth() > depth - (tte->bound() == BOUND_EXACT) && ttValue != VALUE_NONE // Possible in case of TT access race && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) @@ -664,7 +666,7 @@ namespace { } // Step 5. Tablebases probe - if (!rootNode && TB::Cardinality) + if (!rootNode && !excludedMove && TB::Cardinality) { int piecesCount = pos.count(); @@ -727,6 +729,12 @@ namespace { complexity = 0; goto moves_loop; } + else if (excludedMove) { + // excludeMove implies that we had a ttHit on the containing non-excluded search with ss->staticEval filled from TT + // However static evals from the TT aren't good enough (-13 elo), presumably due to changing optimism context + // Recalculate value with current optimism (without updating thread avgComplexity) + ss->staticEval = eval = evaluate(pos, &complexity); + } else if (ss->ttHit) { // Never assume anything about values stored in TT @@ -744,12 +752,9 @@ namespace { else { ss->staticEval = eval = evaluate(pos, &complexity); - // Save static evaluation into transposition table - if (!excludedMove) - tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); + tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } - thisThread->complexityAverage.update(complexity); // Use static evaluation difference to improve quiet move ordering (~4 Elo) @@ -1061,6 +1066,7 @@ moves_loop: // When in check, search starts here Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; + // the search with excludedMove will update ss->staticEval value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); ss->excludedMove = MOVE_NONE; @@ -1214,9 +1220,6 @@ moves_loop: // When in check, search starts here int bonus = value > alpha ? stat_bonus(newDepth) : -stat_bonus(newDepth); - if (capture) - bonus /= 6; - update_continuation_histories(ss, movedPiece, to_sq(move), bonus); } } @@ -1422,6 +1425,7 @@ moves_loop: // When in check, search starts here bool pvHit, givesCheck, capture; int moveCount; + // Step 1. Initialize node if (PvNode) { (ss+1)->pv = pv; @@ -1433,7 +1437,7 @@ moves_loop: // When in check, search starts here ss->inCheck = pos.checkers(); moveCount = 0; - // Check for an immediate draw or maximum ply reached + // Step 2. Check for an immediate draw or maximum ply reached if ( pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) : VALUE_DRAW; @@ -1445,13 +1449,14 @@ moves_loop: // When in check, search starts here // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS. ttDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NO_CHECKS; - // Transposition table lookup + // Step 3. Transposition table lookup posKey = pos.key(); tte = TT.probe(posKey, ss->ttHit); ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = ss->ttHit ? tte->move() : MOVE_NONE; pvHit = ss->ttHit && tte->is_pv(); + // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit && tte->depth() >= ttDepth @@ -1459,7 +1464,7 @@ moves_loop: // When in check, search starts here && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) return ttValue; - // Evaluate the position statically + // Step 4. Static evaluation of the position if (ss->inCheck) { ss->staticEval = VALUE_NONE; @@ -1517,7 +1522,8 @@ moves_loop: // When in check, search starts here int quietCheckEvasions = 0; - // Loop through the moves until no moves remain or a beta cutoff occurs + // Step 5. Loop through all pseudo-legal moves until no moves remain + // or a beta cutoff occurs. while ((move = mp.next_move()) != MOVE_NONE) { assert(is_ok(move)); @@ -1531,9 +1537,11 @@ moves_loop: // When in check, search starts here moveCount++; + // Step 6. Pruning. + if (bestValue > VALUE_TB_LOSS_IN_MAX_PLY) + { // Futility pruning and moveCount pruning (~10 Elo) - if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY - && !givesCheck + if ( !givesCheck && to_sq(move) != prevSq && futilityBase > -VALUE_KNOWN_WIN && type_of(move) != PROMOTION) @@ -1556,43 +1564,43 @@ moves_loop: // When in check, search starts here } } - // Do not search moves with negative SEE values (~5 Elo) - if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY - && !pos.see_ge(move)) + // We prune after 2nd quiet check evasion where being 'in check' is implicitly checked through the counter + // and being a 'quiet' apart from being a tt move is assumed after an increment because captures are pushed ahead. + if (quietCheckEvasions > 1) + break; + + // Continuation history based pruning (~3 Elo) + if ( !capture + && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 + && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0) + continue; + + // Do not search moves with bad enough SEE values (~5 Elo) + if (!pos.see_ge(move, Value(-108))) continue; + } + // Speculative prefetch as early as possible prefetch(TT.first_entry(pos.key_after(move))); + // Update the current move ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] [capture] [pos.moved_piece(move)] [to_sq(move)]; - // Continuation history based pruning (~3 Elo) - if ( !capture - && bestValue > VALUE_TB_LOSS_IN_MAX_PLY - && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 - && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0) - continue; - - // We prune after 2nd quiet check evasion where being 'in check' is implicitly checked through the counter - // and being a 'quiet' apart from being a tt move is assumed after an increment because captures are pushed ahead. - if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY - && quietCheckEvasions > 1) - break; - quietCheckEvasions += !capture && ss->inCheck; - // Make and search the move + // Step 7. Make and search the move pos.do_move(move, st, givesCheck); value = -qsearch(pos, ss+1, -beta, -alpha, depth - 1); pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); - // Check for a new best move + // Step 8. Check for a new best move if (value > bestValue) { bestValue = value; @@ -1612,6 +1620,7 @@ moves_loop: // When in check, search starts here } } + // Step 9. Check for mate // All legal moves have been searched. A special case: if we're in check // and no legal moves were found, it is checkmate. if (ss->inCheck && bestValue == -VALUE_INFINITE)