X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=220fff5e10d03e50e74e4052c2f3c9f97bbfd380;hp=a10e1180c344a3af45a23bf293386d27cc48ddbf;hb=4b703b142946ae03f03ca75738d927fd44a69b39;hpb=02420d4670e54f41bce5bc6d53fb437b80f9d534 diff --git a/src/search.cpp b/src/search.cpp index a10e1180..220fff5e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -155,21 +155,17 @@ void Search::init() { size_t Search::perft(Position& pos, Depth depth) { - // At the last ply just return the number of legal moves (leaf nodes) - if (depth == ONE_PLY) - return MoveList(pos).size(); - StateInfo st; size_t cnt = 0; CheckInfo ci(pos); + const bool leaf = depth == 2 * ONE_PLY; for (MoveList it(pos); *it; ++it) { pos.do_move(*it, st, ci, pos.move_gives_check(*it, ci)); - cnt += perft(pos, depth - ONE_PLY); + cnt += leaf ? MoveList(pos).size() : perft(pos, depth - ONE_PLY); pos.undo_move(*it); } - return cnt; } @@ -300,9 +296,12 @@ namespace { Value bestValue, alpha, beta, delta; memset(ss-1, 0, 4 * sizeof(Stack)); - depth = BestMoveChanges = 0; - bestValue = delta = -VALUE_INFINITE; (ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains + + depth = BestMoveChanges = 0; + bestValue = delta = alpha = -VALUE_INFINITE; + beta = VALUE_INFINITE; + TT.new_search(); History.clear(); Gains.clear(); @@ -332,17 +331,12 @@ namespace { // MultiPV loop. We perform a full root search for each PV line for (PVIdx = 0; PVIdx < PVSize; PVIdx++) { - // Set aspiration window default width - if (depth >= 5 && abs(RootMoves[PVIdx].prevScore) < VALUE_KNOWN_WIN) + // Reset aspiration window starting size + if (depth >= 5) { delta = Value(16); - alpha = RootMoves[PVIdx].prevScore - delta; - beta = RootMoves[PVIdx].prevScore + delta; - } - else - { - alpha = -VALUE_INFINITE; - beta = VALUE_INFINITE; + alpha = std::max(RootMoves[PVIdx].prevScore - delta,-VALUE_INFINITE); + beta = std::min(RootMoves[PVIdx].prevScore + delta, VALUE_INFINITE); } // Start with a small aspiration window and, in case of fail high/low, @@ -370,35 +364,28 @@ namespace { if (Signals.stop) return; - // In case of failing high/low increase aspiration window and + // In case of failing low/high increase aspiration window and // research, otherwise exit the loop. - if (bestValue > alpha && bestValue < beta) - break; - - // Give some update (without cluttering the UI) before to research - if (Time::now() - SearchTime > 3000) - sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; - - if (abs(bestValue) >= VALUE_KNOWN_WIN) + if (bestValue <= alpha) { - alpha = -VALUE_INFINITE; - beta = VALUE_INFINITE; + alpha = std::max(bestValue - delta, -VALUE_INFINITE); + + Signals.failedLowAtRoot = true; + Signals.stopOnPonderhit = false; } else if (bestValue >= beta) - { - beta += delta; - delta += delta / 2; - } + beta = std::min(bestValue + delta, VALUE_INFINITE); + else - { - Signals.failedLowAtRoot = true; - Signals.stopOnPonderhit = false; + break; - alpha -= delta; - delta += delta / 2; - } + delta += delta / 2; assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); + + // Give some update (without cluttering the UI) before to research + if (Time::now() - SearchTime > 3000) + sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; } // Sort the PV lines searched so far and update the GUI @@ -495,7 +482,7 @@ namespace { assert(PvNode || (alpha == beta - 1)); assert(depth > DEPTH_ZERO); - Move movesSearched[64]; + Move quietsSearched[64]; StateInfo st; const TTEntry *tte; SplitPoint* splitPoint; @@ -506,11 +493,11 @@ namespace { Value eval, nullValue, futilityValue; bool inCheck, givesCheck, pvMove, singularExtensionNode; bool captureOrPromotion, dangerous, doFullDepthSearch; - int moveCount, playedMoveCount; + int moveCount, quietCount; // Step 1. Initialize node Thread* thisThread = pos.this_thread(); - moveCount = playedMoveCount = 0; + moveCount = quietCount = 0; inCheck = pos.checkers(); if (SpNode) @@ -574,9 +561,9 @@ namespace { && tte && tte->depth() >= depth && ttValue != VALUE_NONE // Only in case of TT access race - && ( PvNode ? tte->type() == BOUND_EXACT - : ttValue >= beta ? (tte->type() & BOUND_LOWER) - : (tte->type() & BOUND_UPPER))) + && ( PvNode ? tte->bound() == BOUND_EXACT + : ttValue >= beta ? (tte->bound() & BOUND_LOWER) + : (tte->bound() & BOUND_UPPER))) { TT.refresh(tte); ss->currentMove = ttMove; // Can be MOVE_NONE @@ -594,7 +581,10 @@ namespace { // Step 5. Evaluate the position statically and update parent's gain statistics if (inCheck) + { ss->staticEval = ss->evalMargin = eval = VALUE_NONE; + goto iid_start; + } else if (tte) { @@ -605,8 +595,8 @@ namespace { // Can ttValue be used as a better position evaluation? if (ttValue != VALUE_NONE) - if ( ((tte->type() & BOUND_LOWER) && ttValue > eval) - || ((tte->type() & BOUND_UPPER) && ttValue < eval)) + if ( ((tte->bound() & BOUND_LOWER) && ttValue > eval) + || ((tte->bound() & BOUND_UPPER) && ttValue < eval)) eval = ttValue; } else @@ -618,10 +608,10 @@ 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)->staticEval != VALUE_NONE + if ( !pos.captured_piece_type() && ss->staticEval != VALUE_NONE - && !pos.captured_piece_type() + && (ss-1)->staticEval != VALUE_NONE + && (move = (ss-1)->currentMove) != MOVE_NULL && type_of(move) == NORMAL) { Square to = to_sq(move); @@ -631,7 +621,6 @@ namespace { // Step 6. Razoring (is omitted in PV nodes) if ( !PvNode && depth < 4 * ONE_PLY - && !inCheck && eval + razor_margin(depth) < beta && ttMove == MOVE_NONE && abs(beta) < VALUE_MATE_IN_MAX_PLY @@ -651,7 +640,6 @@ namespace { if ( !PvNode && !ss->skipNullMove && depth < 4 * ONE_PLY - && !inCheck && eval - futility_margin(depth, (ss-1)->futilityMoveCount) >= beta && abs(beta) < VALUE_MATE_IN_MAX_PLY && abs(eval) < VALUE_KNOWN_WIN @@ -662,7 +650,6 @@ namespace { if ( !PvNode && !ss->skipNullMove && depth > ONE_PLY - && !inCheck && eval >= beta && abs(beta) < VALUE_MATE_IN_MAX_PLY && pos.non_pawn_material(pos.side_to_move())) @@ -724,9 +711,7 @@ namespace { // prune the previous move. if ( !PvNode && depth >= 5 * ONE_PLY - && !inCheck && !ss->skipNullMove - && excludedMove == MOVE_NONE && abs(beta) < VALUE_MATE_IN_MAX_PLY) { Value rbeta = beta + 200; @@ -751,6 +736,8 @@ namespace { } } +iid_start: // When in check we skip early cut tests + // Step 10. Internal iterative deepening if ( depth >= (PvNode ? 5 * ONE_PLY : 8 * ONE_PLY) && ttMove == MOVE_NONE @@ -780,7 +767,7 @@ split_point_start: // At split points actual search starts from here && depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY) && ttMove != MOVE_NONE && !excludedMove // Recursive singular search is not allowed - && (tte->type() & BOUND_LOWER) + && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3 * ONE_PLY; // Step 11. Loop through moves @@ -825,12 +812,7 @@ split_point_start: // At split points actual search starts from here givesCheck = pos.move_gives_check(move, ci); dangerous = givesCheck || pos.is_passed_pawn_push(move) - || type_of(move) == CASTLE - || ( captureOrPromotion // Entering a pawn endgame? - && type_of(pos.piece_on(to_sq(move))) != PAWN - && type_of(move) == NORMAL - && ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) - - PieceValue[MG][pos.piece_on(to_sq(move))] == VALUE_ZERO)); + || type_of(move) == CASTLE; // Step 12. Extend checks and, in PV nodes, also dangerous moves if (PvNode && dangerous) @@ -931,8 +913,8 @@ split_point_start: // At split points actual search starts from here pvMove = PvNode && moveCount == 1; ss->currentMove = move; - if (!SpNode && !captureOrPromotion && playedMoveCount < 64) - movesSearched[playedMoveCount++] = move; + if (!SpNode && !captureOrPromotion && quietCount < 64) + quietsSearched[quietCount++] = move; // Step 14. Make the move pos.do_move(move, st, ci, givesCheck); @@ -1083,43 +1065,37 @@ split_point_start: // At split points actual search starts from here // If we have pruned all the moves without searching return a fail-low score if (bestValue == -VALUE_INFINITE) - { - assert(!playedMoveCount); - bestValue = alpha; - } - if (bestValue >= beta) // Failed high + TT.store(posKey, value_to_tt(bestValue, ss->ply), + bestValue >= beta ? BOUND_LOWER : + PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, + depth, bestMove, ss->staticEval, ss->evalMargin); + + // Quiet best move: update killers, history and countermoves + if ( bestValue >= beta + && !pos.is_capture_or_promotion(bestMove) + && !inCheck) { - TT.store(posKey, value_to_tt(bestValue, ss->ply), BOUND_LOWER, depth, - bestMove, ss->staticEval, ss->evalMargin); - - if (!pos.is_capture_or_promotion(bestMove) && !inCheck) + if (ss->killers[0] != bestMove) { - if (bestMove != ss->killers[0]) - { - ss->killers[1] = ss->killers[0]; - ss->killers[0] = bestMove; - } - - // Increase history value of the cut-off move - Value bonus = Value(int(depth) * int(depth)); - History.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus); - if (is_ok((ss-1)->currentMove)) - Countermoves.update(pos.piece_on(prevMoveSq), prevMoveSq, bestMove); + ss->killers[1] = ss->killers[0]; + ss->killers[0] = bestMove; + } - // Decrease history of all the other played non-capture moves - for (int i = 0; i < playedMoveCount - 1; i++) - { - Move m = movesSearched[i]; - History.update(pos.piece_moved(m), to_sq(m), -bonus); - } + // Increase history value of the cut-off move and decrease all the other + // 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++) + { + Move m = quietsSearched[i]; + History.update(pos.piece_moved(m), to_sq(m), -bonus); } + + if (is_ok((ss-1)->currentMove)) + Countermoves.update(pos.piece_on(prevMoveSq), prevMoveSq, bestMove); } - else // Failed low or PV search - TT.store(posKey, value_to_tt(bestValue, ss->ply), - PvNode && bestMove != MOVE_NONE ? BOUND_EXACT : BOUND_UPPER, - depth, bestMove, ss->staticEval, ss->evalMargin); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1167,8 +1143,7 @@ split_point_start: // At split points actual search starts from here ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NO_CHECKS; - // Transposition table lookup. At PV nodes, we don't use the TT for - // pruning, but only for move ordering. + // Transposition table lookup posKey = pos.key(); tte = TT.probe(posKey); ttMove = tte ? tte->move() : MOVE_NONE; @@ -1177,9 +1152,9 @@ split_point_start: // At split points actual search starts from here if ( tte && tte->depth() >= ttDepth && ttValue != VALUE_NONE // Only in case of TT access race - && ( PvNode ? tte->type() == BOUND_EXACT - : ttValue >= beta ? (tte->type() & BOUND_LOWER) - : (tte->type() & BOUND_UPPER))) + && ( PvNode ? tte->bound() == BOUND_EXACT + : ttValue >= beta ? (tte->bound() & BOUND_LOWER) + : (tte->bound() & BOUND_UPPER))) { ss->currentMove = ttMove; // Can be MOVE_NONE return ttValue; @@ -1589,7 +1564,7 @@ split_point_start: // At split points actual search starts from here void RootMove::extract_pv_from_tt(Position& pos) { StateInfo state[MAX_PLY_PLUS_2], *st = state; - TTEntry* tte; + const TTEntry* tte; int ply = 0; Move m = pv[0]; @@ -1622,7 +1597,7 @@ void RootMove::extract_pv_from_tt(Position& pos) { void RootMove::insert_pv_in_tt(Position& pos) { StateInfo state[MAX_PLY_PLUS_2], *st = state; - TTEntry* tte; + const TTEntry* tte; int ply = 0; do {