X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=214177a2afae1280eb296352fe59245dfd2c98cb;hp=34a478c54d33a7dc380d9a0c8505ef949a730915;hb=5451687efbb71e93a063a4d323cc9f73ba12e9a4;hpb=486c8175c4f48381b137e3d914d38d9bb8c39ba2 diff --git a/src/search.cpp b/src/search.cpp index 34a478c5..214177a2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -67,8 +67,7 @@ namespace { const int skipPhase[] = { 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7 }; // Razoring and futility margin based on depth - // razor_margin[0] is unused as long as depth >= ONE_PLY in search - const int razor_margin[] = { 0, 570, 603, 554 }; + const int razor_margin = 600; Value futility_margin(Depth d) { return Value(150 * d / ONE_PLY); } // Futility and reductions lookup tables, initialized at startup @@ -96,8 +95,6 @@ namespace { Move best = MOVE_NONE; }; - Value DrawValue[COLOR_NB]; - template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode, bool skipEarlyPruning); @@ -109,6 +106,7 @@ namespace { void update_pv(Move* pv, Move move, Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); void update_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, int bonus); + void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCnt, int bonus); bool pv_is_draw(Position& pos); // perft() is our utility to verify move generation. All the leaf nodes up @@ -174,13 +172,7 @@ void Search::clear() { Time.availableNodes = 0; TT.clear(); - - for (Thread* th : Threads) - th->clear(); - - Threads.main()->callsCnt = 0; - Threads.main()->previousScore = VALUE_INFINITE; - Threads.main()->previousTimeReduction = 1; + Threads.clear(); } @@ -201,8 +193,9 @@ void MainThread::search() { TT.new_search(); int contempt = Options["Contempt"] * PawnValueEg / 100; // From centipawns - DrawValue[ us] = VALUE_DRAW - Value(contempt); - DrawValue[~us] = VALUE_DRAW + Value(contempt); + + Eval::Contempt = (us == WHITE ? make_score(contempt, contempt / 2) + : -make_score(contempt, contempt / 2)); if (rootMoves.empty()) { @@ -443,7 +436,7 @@ void Thread::search() { int improvingFactor = std::max(229, std::min(715, 357 + 119 * F[0] - 6 * F[1])); Color us = rootPos.side_to_move(); - bool thinkHard = DrawValue[us] == bestValue + bool thinkHard = bestValue == VALUE_DRAW && Limits.time[us] - Time.elapsed() > Limits.time[~us] && ::pv_is_draw(rootPos); @@ -500,7 +493,7 @@ namespace { assert(!(PvNode && cutNode)); assert(depth / ONE_PLY * ONE_PLY == depth); - Move pv[MAX_PLY+1], quietsSearched[64]; + Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64]; StateInfo st; TTEntry* tte; Key posKey; @@ -510,12 +503,12 @@ namespace { bool ttHit, inCheck, givesCheck, singularExtensionNode, improving; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture, pvExact; Piece movedPiece; - int moveCount, quietCount; + int moveCount, captureCount, quietCount; // Step 1. Initialize node Thread* thisThread = pos.this_thread(); inCheck = pos.checkers(); - moveCount = quietCount = ss->moveCount = 0; + moveCount = captureCount = quietCount = ss->moveCount = 0; ss->statScore = 0; bestValue = -VALUE_INFINITE; @@ -531,8 +524,7 @@ namespace { { // Step 2. Check for aborted search and immediate draw if (Threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) - return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos) - : DrawValue[pos.side_to_move()]; + return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos) : VALUE_DRAW; // Step 3. Mate distance pruning. Even if we mate at the next move our score // would be at best mate_in(ss->ply+1), but if alpha is already bigger because @@ -558,7 +550,7 @@ namespace { // search to overwrite a previous full search TT value, so we use a different // position key in case of an excluded move. excludedMove = ss->excludedMove; - posKey = pos.key() ^ Key(excludedMove); + posKey = pos.key() ^ Key(excludedMove << 16); // isn't a very good hash tte = TT.probe(posKey, ttHit); ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->PVIdx].pv[0] @@ -655,18 +647,18 @@ namespace { ss->staticEval, TT.generation()); } - if (skipEarlyPruning) + if (skipEarlyPruning || !pos.non_pawn_material(pos.side_to_move())) goto moves_loop; // Step 6. Razoring (skipped when in check) if ( !PvNode && depth < 4 * ONE_PLY - && eval + razor_margin[depth / ONE_PLY] <= alpha) + && eval + razor_margin <= alpha) { if (depth <= ONE_PLY) return qsearch(pos, ss, alpha, alpha+1); - Value ralpha = alpha - razor_margin[depth / ONE_PLY]; + Value ralpha = alpha - razor_margin; Value v = qsearch(pos, ss, ralpha, ralpha+1); if (v <= ralpha) return v; @@ -676,15 +668,14 @@ namespace { if ( !rootNode && depth < 7 * ONE_PLY && eval - futility_margin(depth) >= beta - && eval < VALUE_KNOWN_WIN // Do not return unproven wins - && pos.non_pawn_material(pos.side_to_move())) + && eval < VALUE_KNOWN_WIN) // Do not return unproven wins return eval; // Step 8. Null move search with verification search (is omitted in PV nodes) if ( !PvNode && eval >= beta - && (ss->staticEval >= beta - 35 * (depth / ONE_PLY - 6) || depth >= 13 * ONE_PLY) - && pos.non_pawn_material(pos.side_to_move())) + && ss->staticEval >= beta - 36 * depth / ONE_PLY + 225 + && (ss->ply >= thisThread->nmp_ply || ss->ply % 2 != thisThread->nmp_odd)) { assert(eval - beta >= 0); @@ -706,13 +697,19 @@ namespace { if (nullValue >= VALUE_MATE_IN_MAX_PLY) nullValue = beta; - if (depth < 12 * ONE_PLY && abs(beta) < VALUE_KNOWN_WIN) + if (abs(beta) < VALUE_KNOWN_WIN && (depth < 12 * ONE_PLY || thisThread->nmp_ply)) return nullValue; // Do verification search at high depths + // disable null move pruning for side to move for the first part of the remaining search tree + thisThread->nmp_ply = ss->ply + 3 * (depth-R) / 4; + thisThread->nmp_odd = ss->ply % 2; + Value v = depth-R < ONE_PLY ? qsearch(pos, ss, beta-1, beta) : search(pos, ss, beta-1, beta, depth-R, false, true); + thisThread->nmp_odd = thisThread->nmp_ply = 0; + if (v >= beta) return nullValue; } @@ -729,7 +726,7 @@ namespace { assert(is_ok((ss-1)->currentMove)); - MovePicker mp(pos, ttMove, rbeta - ss->staticEval); + MovePicker mp(pos, ttMove, rbeta - ss->staticEval, &thisThread->captureHistory); while ((move = mp.next_move()) != MOVE_NONE) if (pos.legal(move)) @@ -763,7 +760,7 @@ moves_loop: // When in check search starts from here const PieceToHistory* contHist[] = { (ss-1)->contHistory, (ss-2)->contHistory, nullptr, (ss-4)->contHistory }; Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq]; - MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, contHist, countermove, ss->killers); + MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, countermove, ss->killers); value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc improving = ss->staticEval >= (ss-2)->staticEval /* || ss->staticEval == VALUE_NONE Already implicit in the previous condition */ @@ -811,7 +808,7 @@ moves_loop: // When in check search starts from here movedPiece = pos.moved_piece(move); givesCheck = type_of(move) == NORMAL && !pos.discovered_check_candidates() - ? pos.check_squares(type_of(pos.piece_on(from_sq(move)))) & to_sq(move) + ? pos.check_squares(type_of(movedPiece)) & to_sq(move) : pos.gives_check(move); moveCountPruning = depth < 16 * ONE_PLY @@ -1054,6 +1051,8 @@ moves_loop: // When in check search starts from here if (!captureOrPromotion && move != bestMove && quietCount < 64) quietsSearched[quietCount++] = move; + else if (captureOrPromotion && move != bestMove && captureCount < 32) + capturesSearched[captureCount++] = move; } // The following condition would detect a stop only after move loop has been @@ -1073,12 +1072,14 @@ moves_loop: // When in check search starts from here if (!moveCount) bestValue = excludedMove ? alpha - : inCheck ? mated_in(ss->ply) : DrawValue[pos.side_to_move()]; + : inCheck ? mated_in(ss->ply) : VALUE_DRAW; else if (bestMove) { // Quiet best move: update move sorting heuristics if (!pos.capture_or_promotion(bestMove)) update_stats(pos, ss, bestMove, quietsSearched, quietCount, stat_bonus(depth)); + else + update_capture_stats(pos, bestMove, capturesSearched, captureCount, stat_bonus(depth)); // Extra penalty for a quiet TT move in previous ply when it gets refuted if ((ss-1)->moveCount == 1 && !pos.captured_piece()) @@ -1110,7 +1111,7 @@ moves_loop: // When in check search starts from here const bool PvNode = NT == PV; - assert(InCheck == !!pos.checkers()); + assert(InCheck == bool(pos.checkers())); assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); assert(depth <= DEPTH_ZERO); @@ -1139,8 +1140,7 @@ moves_loop: // When in check search starts from here // Check for an instant draw or if the maximum ply has been reached if (pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) - return ss->ply >= MAX_PLY && !InCheck ? evaluate(pos) - : DrawValue[pos.side_to_move()]; + return ss->ply >= MAX_PLY && !InCheck ? evaluate(pos) : VALUE_DRAW; assert(0 <= ss->ply && ss->ply < MAX_PLY); @@ -1191,7 +1191,7 @@ moves_loop: // When in check search starts from here if (bestValue >= beta) { if (!ttHit) - tte->save(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER, + tte->save(posKey, value_to_tt(bestValue, ss->ply), BOUND_LOWER, DEPTH_NONE, MOVE_NONE, ss->staticEval, TT.generation()); return bestValue; @@ -1207,7 +1207,7 @@ moves_loop: // When in check search starts from here // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will // be generated. - MovePicker mp(pos, ttMove, depth, &pos.this_thread()->mainHistory, to_sq((ss-1)->currentMove)); + MovePicker mp(pos, ttMove, depth, &pos.this_thread()->mainHistory, &pos.this_thread()->captureHistory, to_sq((ss-1)->currentMove)); // Loop through the moves until no moves remain or a beta cutoff occurs while ((move = mp.next_move()) != MOVE_NONE) @@ -1215,7 +1215,7 @@ moves_loop: // When in check search starts from here assert(is_ok(move)); givesCheck = type_of(move) == NORMAL && !pos.discovered_check_candidates() - ? pos.check_squares(type_of(pos.piece_on(from_sq(move)))) & to_sq(move) + ? pos.check_squares(type_of(pos.moved_piece(move))) & to_sq(move) : pos.gives_check(move); moveCount++; @@ -1251,7 +1251,6 @@ moves_loop: // When in check search starts from here // Don't search moves with negative SEE values if ( (!InCheck || evasionPrunable) - && type_of(move) != PROMOTION && !pos.see_ge(move)) continue; @@ -1362,6 +1361,26 @@ moves_loop: // When in check search starts from here } + // update_capture_stats() updates move sorting heuristics when a new capture best move is found + + void update_capture_stats(const Position& pos, Move move, + Move* captures, int captureCnt, int bonus) { + + CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory; + Piece moved_piece = pos.moved_piece(move); + PieceType captured = type_of(pos.piece_on(to_sq(move))); + captureHistory.update(moved_piece, to_sq(move), captured, bonus); + + // Decrease all the other played capture moves + for (int i = 0; i < captureCnt; ++i) + { + moved_piece = pos.moved_piece(captures[i]); + captured = type_of(pos.piece_on(to_sq(captures[i]))); + captureHistory.update(moved_piece, to_sq(captures[i]), captured, -bonus); + } + } + + // update_stats() updates move sorting heuristics when a new quiet best move is found void update_stats(const Position& pos, Stack* ss, Move move, @@ -1473,7 +1492,7 @@ moves_loop: // When in check search starts from here if (Threads.ponder) return; - if ( (Limits.use_time_management() && elapsed > Time.maximum()) + if ( (Limits.use_time_management() && elapsed > Time.maximum() - 10) || (Limits.movetime && elapsed >= Limits.movetime) || (Limits.nodes && Threads.nodes_searched() >= (uint64_t)Limits.nodes)) Threads.stop = true; @@ -1572,10 +1591,6 @@ void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves) ProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY; Cardinality = Options["SyzygyProbeLimit"]; - // Don't filter any moves if the user requested analysis on multiple - if (Options["MultiPV"] != 1) - return; - // Skip TB probing when no TB found: !TBLargest -> !TB::Cardinality if (Cardinality > MaxCardinality) { @@ -1586,6 +1601,10 @@ void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves) if (Cardinality < popcount(pos.pieces()) || pos.can_castle(ANY_CASTLING)) return; + // Don't filter any moves if the user requested analysis on multiple + if (Options["MultiPV"] != 1) + return; + // If the current root position is in the tablebases, then RootMoves // contains only moves that preserve the draw or the win. RootInTB = root_probe(pos, rootMoves, TB::Score);