X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=097faf90f7c2fc4d15f5845335c306fa792ae99f;hp=be82568a2179f03eb9bce512617950a7abad8f50;hb=41641e3b1eea0038ab6984766d2b3bca869be7fa;hpb=f14cd1bb89d080f36a11df3c90f15da881c599df diff --git a/src/search.cpp b/src/search.cpp index be82568a..097faf90 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -83,7 +83,7 @@ namespace { Value DrawValue[COLOR_NB]; HistoryStats History; GainsStats Gains; - CountermovesStats Countermoves; + MovesStats Countermoves, Followupmoves; template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); @@ -154,10 +154,10 @@ void Search::init() { /// Search::perft() is our utility to verify move generation. All the leaf nodes /// up to the given depth are generated and counted and the sum returned. -static size_t perft(Position& pos, Depth depth) { +static uint64_t perft(Position& pos, Depth depth) { StateInfo st; - size_t cnt = 0; + uint64_t cnt = 0; CheckInfo ci(pos); const bool leaf = depth == 2 * ONE_PLY; @@ -170,7 +170,7 @@ static size_t perft(Position& pos, Depth depth) { return cnt; } -size_t Search::perft(Position& pos, Depth depth) { +uint64_t Search::perft(Position& pos, Depth depth) { return depth > ONE_PLY ? ::perft(pos, depth) : MoveList(pos).size(); } @@ -304,6 +304,7 @@ namespace { History.clear(); Gains.clear(); Countermoves.clear(); + Followupmoves.clear(); PVSize = Options["MultiPV"]; Skill skill(Options["Skill Level"]); @@ -499,7 +500,7 @@ namespace { moveCount = quietCount = 0; bestValue = -VALUE_INFINITE; - ss->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; + ss->currentMove = ss->ttMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; ss->ply = (ss-1)->ply + 1; (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; @@ -532,7 +533,7 @@ namespace { excludedMove = ss->excludedMove; posKey = excludedMove ? pos.exclusion_key() : pos.key(); tte = TT.probe(posKey); - ttMove = RootNode ? RootMoves[PVIdx].pv[0] : tte ? tte->move() : MOVE_NONE; + ss->ttMove = ttMove = RootNode ? RootMoves[PVIdx].pv[0] : tte ? tte->move() : MOVE_NONE; ttValue = tte ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; // At PV nodes we check for exact scores, whilst at non-PV nodes we check for @@ -550,7 +551,7 @@ namespace { TT.refresh(tte); ss->currentMove = ttMove; // Can be MOVE_NONE - // If ttMove is quiet, update killers, history, and counter move on TT hit + // If ttMove is quiet, update killers, history, counter move and followup move on TT hit if (ttValue >= beta && ttMove && !pos.capture_or_promotion(ttMove) && !inCheck) update_stats(pos, ss, ttMove, depth, NULL, 0); @@ -594,16 +595,14 @@ namespace { // Step 6. Razoring (skipped when in check) if ( !PvNode && depth < 4 * ONE_PLY - && eval + razor_margin(depth) < beta + && eval + razor_margin(depth) <= alpha && ttMove == MOVE_NONE && abs(beta) < VALUE_MATE_IN_MAX_PLY && !pos.pawn_on_7th(pos.side_to_move())) { - Value rbeta = beta - razor_margin(depth); - Value v = qsearch(pos, ss, rbeta-1, rbeta, DEPTH_ZERO); - if (v < rbeta) - // Logically we should return (v + razor_margin(depth)), but - // surprisingly this performed slightly weaker in tests. + Value ralpha = alpha - razor_margin(depth); + Value v = qsearch(pos, ss, ralpha, ralpha+1, DEPTH_ZERO); + if (v <= ralpha) return v; } @@ -627,37 +626,22 @@ namespace { { ss->currentMove = MOVE_NULL; - // Null move dynamic reduction based on depth - Depth R = 3 * ONE_PLY + depth / 4; + assert(eval - beta >= 0); - // Null move dynamic reduction based on value - if (eval - PawnValueMg > beta) - R += ONE_PLY; + // Null move dynamic reduction based on depth and value + Depth R = 3 * ONE_PLY + + depth / 4 + + int(eval - beta) / PawnValueMg * ONE_PLY; pos.do_null_move(st); (ss+1)->skipNullMove = true; - nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) - : - search(pos, ss+1, -beta, -alpha, depth-R, !cutNode); + nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -beta+1, DEPTH_ZERO) + : - search(pos, ss+1, -beta, -beta+1, depth-R, !cutNode); (ss+1)->skipNullMove = false; pos.undo_null_move(); - if (nullValue >= beta) - { - // Do not return unproven mate scores - if (nullValue >= VALUE_MATE_IN_MAX_PLY) - nullValue = beta; - - if (depth < 12 * ONE_PLY) - return nullValue; - - // Do verification search at high depths - ss->skipNullMove = true; - Value v = search(pos, ss, alpha, beta, depth-R, false); - ss->skipNullMove = false; - - if (v >= beta) - return nullValue; - } + if (nullValue >= beta) // Do not return unproven mate scores + return nullValue >= VALUE_MATE_IN_MAX_PLY ? beta : nullValue; } // Step 9. ProbCut (skipped when in check) @@ -712,7 +696,11 @@ moves_loop: // When in check and at SpNode search starts from here Move countermoves[] = { Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].first, Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].second }; - MovePicker mp(pos, ttMove, depth, History, countermoves, ss); + Square prevOwnMoveSq = to_sq((ss-2)->currentMove); + Move followupmoves[] = { Followupmoves[pos.piece_on(prevOwnMoveSq)][prevOwnMoveSq].first, + Followupmoves[pos.piece_on(prevOwnMoveSq)][prevOwnMoveSq].second }; + + MovePicker mp(pos, ttMove, depth, History, countermoves, followupmoves, ss); CheckInfo ci(pos); value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc improving = ss->staticEval >= (ss-2)->staticEval @@ -766,7 +754,11 @@ moves_loop: // When in check and at SpNode search starts from here ext = DEPTH_ZERO; captureOrPromotion = pos.capture_or_promotion(move); - givesCheck = pos.gives_check(move, ci); + + givesCheck = type_of(move) == NORMAL && !ci.dcCandidates + ? ci.checkSq[type_of(pos.piece_on(from_sq(move)))] & to_sq(move) + : pos.gives_check(move, ci); + dangerous = givesCheck || type_of(move) != NORMAL || pos.advanced_pawn_push(move); @@ -1029,7 +1021,7 @@ moves_loop: // When in check and at SpNode search starts from here PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, depth, bestMove, ss->staticEval); - // Quiet best move: update killers, history and countermoves + // Quiet best move: update killers, history, countermoves and followupmoves if (bestValue >= beta && !pos.capture_or_promotion(bestMove) && !inCheck) update_stats(pos, ss, bestMove, depth, quietsSearched, quietCount - 1); @@ -1146,7 +1138,9 @@ moves_loop: // When in check and at SpNode search starts from here { assert(is_ok(move)); - givesCheck = pos.gives_check(move, ci); + givesCheck = type_of(move) == NORMAL && !ci.dcCandidates + ? ci.checkSq[type_of(pos.piece_on(from_sq(move)))] & to_sq(move) + : pos.gives_check(move, ci); // Futility pruning if ( !PvNode @@ -1264,7 +1258,7 @@ moves_loop: // When in check and at SpNode search starts from here } - // update_stats() updates killers, history and countermoves stats after a fail-high + // update_stats() updates killers, history, countermoves and followupmoves stats after a fail-high // of a quiet move. void update_stats(Position& pos, Stack* ss, Move move, Depth depth, Move* quiets, int quietsCnt) { @@ -1290,6 +1284,12 @@ moves_loop: // When in check and at SpNode search starts from here Square prevMoveSq = to_sq((ss-1)->currentMove); Countermoves.update(pos.piece_on(prevMoveSq), prevMoveSq, move); } + + if (is_ok((ss-2)->currentMove) && (ss-1)->currentMove == (ss-1)->ttMove) + { + Square prevOwnMoveSq = to_sq((ss-2)->currentMove); + Followupmoves.update(pos.piece_on(prevOwnMoveSq), prevOwnMoveSq, move); + } } @@ -1612,8 +1612,9 @@ void check_time() { Time::point elapsed = Time::now() - SearchTime; bool stillAtFirstMove = Signals.firstRootMove && !Signals.failedLowAtRoot - && elapsed > (TimeMgr.available_time() * 62) / 100 - && elapsed > IterationTime * 1.4; + && ( elapsed > TimeMgr.available_time() + || ( elapsed > (TimeMgr.available_time() * 62) / 100 + && elapsed > IterationTime * 1.4)); bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerThread::Resolution || stillAtFirstMove;