X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=ab34a8451cceda529f7996faf4df0500d6715f0c;hp=44c29ec878209a882c27f5880878443ac32a0081;hb=58452de86d120ca7894917c656ecbb56a6273f22;hpb=dd5a3ae4a6c7cc4adcbb4b72378694b0889139d7 diff --git a/src/search.cpp b/src/search.cpp index 44c29ec8..ab34a845 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -172,10 +172,16 @@ namespace { const bool PruneDefendingMoves = false; const bool PruneBlockingMoves = false; + // Only move margin + const Value OnlyMoveMargin = Value(100); + // Margins for futility pruning in the quiescence search, and at frontier // and near frontier nodes. const Value FutilityMarginQS = Value(0x80); + // Each move futility margin is decreased + const Value IncrementalFutilityMargin = Value(0x8); + // Remaining depth: 1 ply 1.5 ply 2 ply 2.5 ply 3 ply 3.5 ply const Value FutilityMargins[12] = { Value(0x100), Value(0x120), Value(0x200), Value(0x220), Value(0x250), Value(0x270), // 4 ply 4.5 ply 5 ply 5.5 ply 6 ply 6.5 ply @@ -274,7 +280,7 @@ namespace { Value id_loop(const Position& pos, Move searchMoves[]); Value root_search(Position& pos, SearchStack ss[], RootMoveList& rml, Value alpha, Value beta); Value search_pv(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID); - Value search(Position& pos, SearchStack ss[], Value beta, Depth depth, int ply, bool allowNullmove, int threadID); + Value search(Position& pos, SearchStack ss[], Value beta, Depth depth, int ply, bool allowNullmove, int threadID, Move forbiddenMove = MOVE_NONE); Value qsearch(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID); void sp_search(SplitPoint* sp, int threadID); void sp_search_pv(SplitPoint* sp, int threadID); @@ -310,7 +316,7 @@ namespace { Value *alpha, Value *beta, Value *bestValue, const Value futilityValue, const Value approximateValue, Depth depth, int *moves, - MovePicker *mp, Bitboard dcCandidates, int master, bool pvNode); + MovePicker *mp, int master, bool pvNode); void wake_sleeping_threads(); #if !defined(_MSC_VER) @@ -333,14 +339,14 @@ namespace { int perft(Position& pos, Depth depth) { Move move; - MovePicker mp = MovePicker(pos, MOVE_NONE, depth, H); int sum = 0; + MovePicker mp = MovePicker(pos, MOVE_NONE, depth, H); // If we are at the last ply we don't need to do and undo // the moves, just to count them. if (depth <= OnePly) // Replace with '<' to test also qsearch { - while ((move = mp.get_next_move()) != MOVE_NONE) sum++; + while (mp.get_next_move()) sum++; return sum; } @@ -348,10 +354,10 @@ int perft(Position& pos, Depth depth) CheckInfo ci(pos); while ((move = mp.get_next_move()) != MOVE_NONE) { - StateInfo st; - pos.do_move(move, st, ci.dcCandidates, pos.move_is_check(move, ci)); - sum += perft(pos, depth - OnePly); - pos.undo_move(move); + StateInfo st; + pos.do_move(move, st, ci, pos.move_is_check(move, ci)); + sum += perft(pos, depth - OnePly); + pos.undo_move(move); } return sum; } @@ -898,13 +904,14 @@ namespace { << " currmovenumber " << i + 1 << std::endl; // Decide search depth for this move + bool moveIsCheck = pos.move_is_check(move); bool captureOrPromotion = pos.move_is_capture_or_promotion(move); bool dangerous; - ext = extension(pos, move, true, captureOrPromotion, pos.move_is_check(move), false, false, &dangerous); + ext = extension(pos, move, true, captureOrPromotion, moveIsCheck, false, false, &dangerous); newDepth = (Iteration - 2) * OnePly + ext + InitialDepth; // Make the move, and search it - pos.do_move(move, st, ci.dcCandidates); + pos.do_move(move, st, ci, moveIsCheck); if (i < MultiPV) { @@ -1128,14 +1135,36 @@ namespace { moveIsCheck = pos.move_is_check(move, ci); captureOrPromotion = pos.move_is_capture_or_promotion(move); - movesSearched[moveCount++] = ss[ply].currentMove = move; + movesSearched[moveCount++] = move; // Decide the new search depth ext = extension(pos, move, true, captureOrPromotion, moveIsCheck, singleReply, mateThreat, &dangerous); + + // Only move extension + if ( moveCount == 1 + && ext < OnePly + && depth >= 4 * OnePly + && tte + && (tte->type() & VALUE_TYPE_LOWER) + && tte->move() != MOVE_NONE + && tte->depth() >= depth - 3 * OnePly) + { + Value ttValue = value_from_tt(tte->value(), ply); + if (abs(ttValue) < VALUE_KNOWN_WIN) + { + Value excValue = search(pos, ss, ttValue - OnlyMoveMargin, Max(Min(depth / 2, depth - 4 * OnePly), OnePly), ply, false, threadID, tte->move()); + if (excValue < ttValue - OnlyMoveMargin) + ext = (depth >= 8 * OnePly)? OnePly : ext + OnePly / 2; + } + } + newDepth = depth - OnePly + ext; + // Update current move + ss[ply].currentMove = move; + // Make and search the move - pos.do_move(move, st, ci.dcCandidates, moveIsCheck); + pos.do_move(move, st, ci, moveIsCheck); if (moveCount == 1) // The first move in list is the PV value = -search_pv(pos, ss, -beta, -alpha, newDepth, ply+1, threadID); @@ -1208,8 +1237,8 @@ namespace { && idle_thread_exists(threadID) && !AbortSearch && !thread_should_stop(threadID) - && split(pos, ss, ply, &alpha, &beta, &bestValue, VALUE_NONE, VALUE_NONE, depth, - &moveCount, &mp, ci.dcCandidates, threadID, true)) + && split(pos, ss, ply, &alpha, &beta, &bestValue, VALUE_NONE, VALUE_NONE, + depth, &moveCount, &mp, threadID, true)) break; } @@ -1247,7 +1276,7 @@ namespace { // search() is the search function for zero-width nodes. Value search(Position& pos, SearchStack ss[], Value beta, Depth depth, - int ply, bool allowNullmove, int threadID) { + int ply, bool allowNullmove, int threadID, Move forbiddenMove) { assert(beta >= -VALUE_INFINITE && beta <= VALUE_INFINITE); assert(ply >= 0 && ply < PLY_MAX); @@ -1259,7 +1288,7 @@ namespace { const TTEntry* tte; Move ttMove, move; Depth ext, newDepth; - Value approximateEval, nullValue, value, futilityValue; + Value approximateEval, nullValue, value, futilityValue, futilityValueScaled; bool isCheck, useFutilityPruning, singleReply, moveIsCheck, captureOrPromotion, dangerous; bool mateThreat = false; int moveCount = 0; @@ -1289,8 +1318,14 @@ namespace { if (value_mate_in(ply + 1) < beta) return beta - 1; + // Position key calculation + Key posKey = pos.get_key(); + + if (forbiddenMove != MOVE_NONE) + posKey ^= Position::zobExclusion; + // Transposition table lookup - tte = TT.retrieve(pos.get_key()); + tte = TT.retrieve(posKey); ttMove = (tte ? tte->move() : MOVE_NONE); if (tte && ok_to_use_TT(tte, depth, beta, ply)) @@ -1313,7 +1348,13 @@ namespace { ss[ply].currentMove = MOVE_NULL; pos.do_null_move(st); - int R = (depth >= 5 * OnePly ? 4 : 3); // Null move dynamic reduction + + // Null move dynamic reduction based on depth + int R = (depth >= 5 * OnePly ? 4 : 3); + + // Null move dynamic reduction based on value + if (approximateEval - beta > PawnValueMidgame) + R++; nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, false, threadID); @@ -1353,8 +1394,9 @@ namespace { && ttMove == MOVE_NONE && !pos.has_pawn_on_7th(pos.side_to_move())) { - Value v = qsearch(pos, ss, beta-1, beta, Depth(0), ply, threadID); - if (v < beta - RazorMargins[int(depth) - 2]) + Value rbeta = beta - RazorMargins[int(depth) - 2]; + Value v = qsearch(pos, ss, rbeta-1, rbeta, Depth(0), ply, threadID); + if (v < rbeta) return v; } @@ -1377,6 +1419,9 @@ namespace { if (tte && (tte->type() & VALUE_TYPE_EVAL)) futilityValue = value_from_tt(tte->value(), ply) + FutilityMargins[int(depth) - 2]; + // Move count pruning limit + const int MCLimit = 3 + (1 << (3*int(depth)/8)); + // Loop through all legal moves until no moves remain or a beta cutoff // occurs. while ( bestValue < beta @@ -1385,23 +1430,97 @@ namespace { { assert(move_is_ok(move)); + if (move == forbiddenMove) + continue; + singleReply = (isCheck && mp.number_of_evasions() == 1); moveIsCheck = pos.move_is_check(move, ci); captureOrPromotion = pos.move_is_capture_or_promotion(move); - movesSearched[moveCount++] = ss[ply].currentMove = move; + movesSearched[moveCount++] = move; // Decide the new search depth ext = extension(pos, move, false, captureOrPromotion, moveIsCheck, singleReply, mateThreat, &dangerous); + + // Only move extension + if ( forbiddenMove == MOVE_NONE + && moveCount == 1 + && ext < OnePly + && depth >= 4 * OnePly + && tte + && (tte->type() & VALUE_TYPE_LOWER) + && tte->move() != MOVE_NONE + && tte->depth() >= depth - 3 * OnePly) + { + Value ttValue = value_from_tt(tte->value(), ply); + if (abs(ttValue) < VALUE_KNOWN_WIN) + { + Value excValue = search(pos, ss, ttValue - OnlyMoveMargin, Max(Min(depth / 2, depth - 4 * OnePly), OnePly), ply, false, threadID, tte->move()); + if (excValue < ttValue - OnlyMoveMargin) + ext = (depth >= 8 * OnePly)? OnePly : ext + OnePly / 2; + } + } + newDepth = depth - OnePly + ext; + // Update current move + ss[ply].currentMove = move; + // Futility pruning if ( useFutilityPruning && !dangerous - && !captureOrPromotion) + && !captureOrPromotion + && move != ttMove) { + //std::cout << std::endl; + //for (int d = 2; d < 14; d++) + // std::cout << d << ", " << 64*(1+bitScanReverse32(d*d)) << std::endl; + + //std::cout << std::endl; +/* + 64*(1+bitScanReverse32(d*d)) + + 2 -> 256 - 256 + 3 -> 288 - 320 + 4 -> 512 - 384 + 5 -> 544 - 384 + 6 -> 592 - 448 + 7 -> 624 - 448 + 8 -> 672 - 512 + 9 -> 704 - 512 + 10 -> 832 - 512 + 11 -> 864 - 512 + 12 -> 928 - 576 + 13 -> 960 - 576 + + 300 + 2*(1 << (3*d/4)) + + 2 -> 256 - 304 + 3 -> 288 - 308 + 4 -> 512 - 316 + 5 -> 544 - 316 + 6 -> 592 - 332 + 7 -> 624 - 364 + 8 -> 672 - 428 + 9 -> 704 - 428 + 10 -> 832 - 556 + 11 -> 864 - 812 + 12 -> 928 - 1324 + 13 -> 960 - 1324 + + + 3 + (1 << (3*int(depth)/8)) + + 1 * onePly - > moveCount >= 4 + 2 * onePly - > moveCount >= 5 + 3 * onePly - > moveCount >= 7 + 4 * onePly - > moveCount >= 11 + 5 * onePly - > moveCount >= 11 + 6 * onePly - > moveCount >= 19 + 7 * onePly - > moveCount >= 35 +*/ // History pruning. See ok_to_prune() definition - if ( moveCount >= 2 + int(depth) + if ( moveCount >= MCLimit && ok_to_prune(pos, move, ss[ply].threatMove, depth) && bestValue > value_mated_in(PLY_MAX)) continue; @@ -1411,19 +1530,21 @@ namespace { { if (futilityValue == VALUE_NONE) futilityValue = evaluate(pos, ei, threadID) - + FutilityMargins[int(depth) - 2]; + + 64*(2+bitScanReverse32(int(depth) * int(depth))); + + futilityValueScaled = futilityValue - moveCount * IncrementalFutilityMargin; - if (futilityValue < beta) + if (futilityValueScaled < beta) { - if (futilityValue > bestValue) - bestValue = futilityValue; + if (futilityValueScaled > bestValue) + bestValue = futilityValueScaled; continue; } } } // Make and search the move - pos.do_move(move, st, ci.dcCandidates, moveIsCheck); + pos.do_move(move, st, ci, moveIsCheck); // Try to reduce non-pv search depth by one ply if move seems not problematic, // if the move fails high will be re-searched at full depth. @@ -1468,15 +1589,15 @@ namespace { && idle_thread_exists(threadID) && !AbortSearch && !thread_should_stop(threadID) - && split(pos, ss, ply, &beta, &beta, &bestValue, futilityValue, approximateEval, depth, &moveCount, - &mp, ci.dcCandidates, threadID, false)) + && split(pos, ss, ply, &beta, &beta, &bestValue, futilityValue, approximateEval, + depth, &moveCount, &mp, threadID, false)) break; } // All legal moves have been searched. A special case: If there were // no legal moves, it must be mate or stalemate. if (moveCount == 0) - return (pos.is_check() ? value_mated_in(ply) : VALUE_DRAW); + return (forbiddenMove == MOVE_NONE ? (pos.is_check() ? value_mated_in(ply) : VALUE_DRAW) : beta - 1); // If the search is not aborted, update the transposition table, // history counters, and killer moves. @@ -1484,7 +1605,7 @@ namespace { return bestValue; if (bestValue < beta) - TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_UPPER, depth, MOVE_NONE); + TT.store(posKey, value_to_tt(bestValue, ply), VALUE_TYPE_UPPER, depth, MOVE_NONE); else { BetaCounter.add(pos.side_to_move(), depth, threadID); @@ -1494,7 +1615,7 @@ namespace { update_history(pos, move, depth, movesSearched, moveCount); update_killers(move, ss[ply]); } - TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, move); + TT.store(posKey, value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, move); } assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1609,6 +1730,7 @@ namespace { && !isCheck && !pvNode && !moveIsCheck + && move != ttMove && !move_is_promotion(move) && !pos.move_is_passed_pawn_push(move)) { @@ -1635,7 +1757,7 @@ namespace { continue; // Make and search the move - pos.do_move(move, st, ci.dcCandidates, moveIsCheck); + pos.do_move(move, st, ci, moveIsCheck); value = -qsearch(pos, ss, -beta, -alpha, depth-OnePly, ply+1, threadID); pos.undo_move(move); @@ -1762,7 +1884,7 @@ namespace { // Make and search the move. StateInfo st; - pos.do_move(move, st, sp->dcCandidates, moveIsCheck); + pos.do_move(move, st, ci, moveIsCheck); // Try to reduce non-pv search depth by one ply if move seems not problematic, // if the move fails high will be re-searched at full depth. @@ -1868,7 +1990,7 @@ namespace { // Make and search the move. StateInfo st; - pos.do_move(move, st, sp->dcCandidates, moveIsCheck); + pos.do_move(move, st, ci, moveIsCheck); // Try to reduce non-pv search depth by one ply if move seems not problematic, // if the move fails high will be re-searched at full depth. @@ -2812,7 +2934,7 @@ namespace { bool split(const Position& p, SearchStack* sstck, int ply, Value* alpha, Value* beta, Value* bestValue, const Value futilityValue, const Value approximateEval, Depth depth, int* moves, - MovePicker* mp, Bitboard dcCandidates, int master, bool pvNode) { + MovePicker* mp, int master, bool pvNode) { assert(p.is_ok()); assert(sstck != NULL); @@ -2849,7 +2971,6 @@ namespace { splitPoint->alpha = pvNode? *alpha : (*beta - 1); splitPoint->beta = *beta; splitPoint->pvNode = pvNode; - splitPoint->dcCandidates = dcCandidates; splitPoint->bestValue = *bestValue; splitPoint->futilityValue = futilityValue; splitPoint->approximateEval = approximateEval;