X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=4a8dd17f81c4b8079ddc745322340a13e8a5691a;hp=5fc9be3679aa1d265b71285ab82b69e4460936eb;hb=5b21c10afba744f2a984fbe4546701e7ffeee241;hpb=860260c3b1c8da9906fddd2385fe127c6f5d10f1 diff --git a/src/search.cpp b/src/search.cpp index 5fc9be36..4a8dd17f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -288,6 +288,7 @@ namespace { bool ok_to_do_nullmove(const Position& pos); bool ok_to_prune(const Position& pos, Move m, Move threat); bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply); + Value refine_eval(const TTEntry* tte, Value defaultEval, int ply); void update_history(const Position& pos, Move move, Depth depth, Move movesSearched[], int moveCount); void update_killers(Move m, SearchStack& ss); @@ -325,6 +326,13 @@ namespace { //// Functions //// +//FIXME: HACK +static double lnArray[512]; + +inline double ln(int i) +{ + return lnArray[i]; +} /// perft() is our utility to verify move generation is bug free. All the legal /// moves up to given depth are generated and counted and the sum returned. @@ -561,8 +569,14 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move, /// and initializes the split point stack and the global locks and condition /// objects. +#include //FIXME: HACK + void init_threads() { + // FIXME: HACK!! + for (int i = 0; i < 512; i++) + lnArray[i] = log(double(i)); + volatile int i; #if !defined(_MSC_VER) @@ -898,7 +912,7 @@ namespace { int64_t nodes; Move move; StateInfo st; - Depth ext, newDepth; + Depth depth, ext, newDepth; RootMoveNumber = i + 1; FailHigh = false; @@ -921,8 +935,9 @@ namespace { bool moveIsCheck = pos.move_is_check(move); bool captureOrPromotion = pos.move_is_capture_or_promotion(move); bool dangerous; + depth = (Iteration - 2) * OnePly + InitialDepth; ext = extension(pos, move, true, captureOrPromotion, moveIsCheck, false, false, &dangerous); - newDepth = (Iteration - 2) * OnePly + ext + InitialDepth; + newDepth = depth + ext; // Make the move, and search it pos.do_move(move, st, ci, moveIsCheck); @@ -949,14 +964,19 @@ namespace { { // 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. - if ( newDepth >= 3*OnePly - && i >= MultiPV + LMRPVMoves + if ( depth >= 3*OnePly // FIXME was newDepth && !dangerous && !captureOrPromotion && !move_is_castle(move)) { - ss[0].reduction = OnePly; - value = -search(pos, ss, -alpha, newDepth-OnePly, 1, true, 0); + double red = 0.5 + ln(RootMoveNumber - MultiPV + 1) * ln(depth / 2) / 6.0; + if (red >= 1.0) + { + ss[0].reduction = Depth(int(floor(red * int(OnePly)))); + value = -search(pos, ss, -alpha, newDepth-ss[0].reduction, 1, true, 0); + } + else + value = alpha + 1; // Just to trigger next condition } else value = alpha + 1; // Just to trigger next condition @@ -1201,14 +1221,19 @@ namespace { // 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. if ( depth >= 3*OnePly - && moveCount >= LMRPVMoves && !dangerous && !captureOrPromotion && !move_is_castle(move) && !move_is_killer(move, ss[ply])) { - ss[ply].reduction = OnePly; - value = -search(pos, ss, -alpha, newDepth-OnePly, ply+1, true, threadID); + double red = 0.5 + ln(moveCount) * ln(depth / 2) / 6.0; + if (red >= 1.0) + { + ss[ply].reduction = Depth(int(floor(red * int(OnePly)))); + value = -search(pos, ss, -alpha, newDepth-ss[ply].reduction, ply+1, true, threadID); + } + else + value = alpha + 1; // Just to trigger next condition } else value = alpha + 1; // Just to trigger next condition @@ -1316,7 +1341,7 @@ namespace { const TTEntry* tte; Move ttMove, move; Depth ext, newDepth; - Value approximateEval, nullValue, value, futilityValue, futilityValueScaled; + Value staticValue, nullValue, value, futilityValue, futilityValueScaled; bool isCheck, useFutilityPruning, singleEvasion, moveIsCheck, captureOrPromotion, dangerous; bool mateThreat = false; int moveCount = 0; @@ -1360,8 +1385,24 @@ namespace { return value_from_tt(tte->value(), ply); } - approximateEval = quick_evaluate(pos); isCheck = pos.is_check(); + ei.futilityMargin = Value(0); // Manually initialize futilityMargin + + // Evaluate the position statically + if (isCheck) + staticValue = quick_evaluate(pos); + else if (tte && (tte->type() & VALUE_TYPE_EVAL)) + staticValue = value_from_tt(tte->value(), ply); + else + staticValue = evaluate(pos, ei, threadID); + + // Calculate depth dependant futility pruning parameters + const int FutilityMoveCountMargin = 3 + (1 << (3 * int(depth) / 8)); + const int FutilityValueMargin = 112 * bitScanReverse32(int(depth) * int(depth) / 2); + + // Enhance score accuracy with TT value if possible + futilityValue = staticValue + FutilityValueMargin; + staticValue = refine_eval(tte, staticValue, ply); // Null move search if ( allowNullmove @@ -1369,7 +1410,7 @@ namespace { && !isCheck && !value_is_mate(beta) && ok_to_do_nullmove(pos) - && approximateEval >= beta - NullMoveMargin) + && staticValue >= beta - NullMoveMargin) { ss[ply].currentMove = MOVE_NULL; @@ -1379,7 +1420,7 @@ namespace { int R = 3 + (depth >= 5 * OnePly ? depth / 8 : 0); // Null move dynamic reduction based on value - if (approximateEval - beta > PawnValueMidgame) + if (staticValue - beta > PawnValueMidgame) R++; nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, false, threadID); @@ -1415,7 +1456,7 @@ namespace { // Null move search not allowed, try razoring else if ( !value_is_mate(beta) && depth < RazorDepth - && approximateEval < beta - RazorApprMargins[int(depth) - 2] + && staticValue < beta - RazorApprMargins[int(depth) - 2] && ss[ply - 1].currentMove != MOVE_NULL && ttMove == MOVE_NONE && !pos.has_pawn_on_7th(pos.side_to_move())) @@ -1439,17 +1480,8 @@ namespace { // to search all moves. MovePicker mp = MovePicker(pos, ttMove, depth, H, &ss[ply]); CheckInfo ci(pos); - futilityValue = VALUE_NONE; useFutilityPruning = depth < SelectiveDepth && !isCheck; - // Calculate depth dependant futility pruning parameters - const int FutilityMoveCountMargin = 3 + (1 << (3 * int(depth) / 8)); - const int FutilityValueMargin = 112 * bitScanReverse32(int(depth) * int(depth) / 2); - - // Avoid calling evaluate() if we already have the score in TT - if (tte && (tte->type() & VALUE_TYPE_EVAL)) - futilityValue = value_from_tt(tte->value(), ply) + FutilityValueMargin; - // Loop through all legal moves until no moves remain or a beta cutoff occurs while ( bestValue < beta && (move = mp.get_next_move()) != MOVE_NONE @@ -1460,8 +1492,8 @@ namespace { if (move == excludedMove) continue; - singleEvasion = (isCheck && mp.number_of_evasions() == 1); moveIsCheck = pos.move_is_check(move, ci); + singleEvasion = (isCheck && mp.number_of_evasions() == 1); captureOrPromotion = pos.move_is_capture_or_promotion(move); // Decide the new search depth @@ -1507,9 +1539,6 @@ namespace { continue; // Value based pruning - if (futilityValue == VALUE_NONE) - futilityValue = evaluate(pos, ei, threadID) + FutilityValueMargin; - futilityValueScaled = futilityValue - moveCount * IncrementalFutilityMargin; if (futilityValueScaled < beta) @@ -1526,14 +1555,20 @@ namespace { // 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. if ( depth >= 3*OnePly - && moveCount >= LMRNonPVMoves && !dangerous && !captureOrPromotion && !move_is_castle(move) - && !move_is_killer(move, ss[ply])) + && !move_is_killer(move, ss[ply]) + /* && move != ttMove*/) { - ss[ply].reduction = OnePly; - value = -search(pos, ss, -(beta-1), newDepth-OnePly, ply+1, true, threadID); + double red = 0.5 + ln(moveCount) * ln(depth / 2) / 3.0; + if (red >= 1.0) + { + ss[ply].reduction = Depth(int(floor(red * int(OnePly)))); + value = -search(pos, ss, -(beta-1), newDepth-ss[ply].reduction, ply+1, true, threadID); + } + else + value = beta; // Just to trigger next condition } else value = beta; // Just to trigger next condition @@ -1571,9 +1606,9 @@ namespace { break; } - // All legal moves have been searched. A special case: If there were + // All legal moves have been searched. A special case: If there were // no legal moves, it must be mate or stalemate. - if (moveCount == 0) + if (!moveCount) return excludedMove ? beta - 1 : (pos.is_check() ? value_mated_in(ply) : VALUE_DRAW); // If the search is not aborted, update the transposition table, @@ -1587,12 +1622,13 @@ namespace { { BetaCounter.add(pos.side_to_move(), depth, threadID); move = ss[ply].pv[ply]; + TT.store(posKey, value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, move); if (!pos.move_is_capture_or_promotion(move)) { update_history(pos, move, depth, movesSearched, moveCount); update_killers(move, ss[ply]); } - TT.store(posKey, value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, move); + } assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1617,7 +1653,7 @@ namespace { EvalInfo ei; StateInfo st; Move ttMove, move; - Value staticValue, bestValue, value, futilityValue; + Value staticValue, bestValue, value, futilityBase, futilityValue; bool isCheck, enoughMaterial, moveIsCheck; const TTEntry* tte = NULL; int moveCount = 0; @@ -1634,18 +1670,21 @@ namespace { if (pos.is_draw()) return VALUE_DRAW; - // Transposition table lookup, only when not in PV - if (!pvNode) + if (ply >= PLY_MAX - 1) + return pos.is_check() ? quick_evaluate(pos) : evaluate(pos, ei, threadID); + + // Transposition table lookup. At PV nodes, we don't use the TT for + // pruning, but only for move ordering. + tte = TT.retrieve(pos.get_key()); + ttMove = (tte ? tte->move() : MOVE_NONE); + + if (!pvNode && tte && ok_to_use_TT(tte, depth, beta, ply)) { - tte = TT.retrieve(pos.get_key()); - if (tte && ok_to_use_TT(tte, depth, beta, ply)) - { - assert(tte->type() != VALUE_TYPE_EVAL); + assert(tte->type() != VALUE_TYPE_EVAL); - return value_from_tt(tte->value(), ply); - } + ss[ply].currentMove = ttMove; // Can be MOVE_NONE + return value_from_tt(tte->value(), ply); } - ttMove = (tte ? tte->move() : MOVE_NONE); isCheck = pos.is_check(); ei.futilityMargin = Value(0); // Manually initialize futilityMargin @@ -1653,20 +1692,11 @@ namespace { // Evaluate the position statically if (isCheck) staticValue = -VALUE_INFINITE; - else if (tte && (tte->type() & VALUE_TYPE_EVAL)) - { - // Use the cached evaluation score if possible - assert(ei.futilityMargin == Value(0)); - staticValue = value_from_tt(tte->value(), ply); - } else staticValue = evaluate(pos, ei, threadID); - if (ply >= PLY_MAX - 1) - return pos.is_check() ? quick_evaluate(pos) : evaluate(pos, ei, threadID); - // Initialize "stand pat score", and return it immediately if it is // at least beta. bestValue = staticValue; @@ -1689,6 +1719,7 @@ namespace { MovePicker mp = MovePicker(pos, ttMove, depth, H); CheckInfo ci(pos); enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame; + futilityBase = staticValue + FutilityMarginQS + ei.futilityMargin; // Loop through the moves until no moves remain or a beta cutoff // occurs. @@ -1697,11 +1728,12 @@ namespace { { assert(move_is_ok(move)); + moveIsCheck = pos.move_is_check(move, ci); + + // Update current move moveCount++; ss[ply].currentMove = move; - moveIsCheck = pos.move_is_check(move, ci); - // Futility pruning if ( enoughMaterial && !isCheck @@ -1711,12 +1743,9 @@ namespace { && !move_is_promotion(move) && !pos.move_is_passed_pawn_push(move)) { - futilityValue = staticValue - + Max(pos.midgame_value_of_piece_on(move_to(move)), - pos.endgame_value_of_piece_on(move_to(move))) - + (move_is_ep(move) ? PawnValueEndgame : Value(0)) - + FutilityMarginQS - + ei.futilityMargin; + futilityValue = futilityBase + + pos.endgame_value_of_piece_on(move_to(move)) + + (move_is_ep(move) ? PawnValueEndgame : Value(0)); if (futilityValue < alpha) { @@ -1752,31 +1781,31 @@ namespace { } } - // All legal moves have been searched. A special case: If we're in check + // All legal moves have been searched. A special case: If we're in check // and no legal moves were found, it is checkmate. if (!moveCount && pos.is_check()) // Mate! return value_mated_in(ply); - assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); - // Update transposition table - move = ss[ply].pv[ply]; - if (!pvNode) + Depth d = (depth == Depth(0) ? Depth(0) : Depth(-1)); + if (bestValue < beta) { - // If bestValue isn't changed it means it is still the static evaluation of - // the node, so keep this info to avoid a future costly evaluation() call. + // If bestValue isn't changed it means it is still the static evaluation + // of the node, so keep this info to avoid a future evaluation() call. ValueType type = (bestValue == staticValue && !ei.futilityMargin ? VALUE_TYPE_EV_UP : VALUE_TYPE_UPPER); - Depth d = (depth == Depth(0) ? Depth(0) : Depth(-1)); + TT.store(pos.get_key(), value_to_tt(bestValue, ply), type, d, MOVE_NONE); + } + else + { + move = ss[ply].pv[ply]; + TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, d, move); - if (bestValue < beta) - TT.store(pos.get_key(), value_to_tt(bestValue, ply), type, d, MOVE_NONE); - else - TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, d, move); + // Update killers only for good checking moves + if (!pos.move_is_capture_or_promotion(move)) + update_killers(move, ss[ply]); } - // Update killers only for good check moves - if (alpha >= beta && !pos.move_is_capture_or_promotion(move)) - update_killers(move, ss[ply]); + assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); return bestValue; } @@ -1867,13 +1896,18 @@ namespace { // 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. if ( !dangerous - && moveCount >= LMRNonPVMoves && !captureOrPromotion && !move_is_castle(move) && !move_is_killer(move, ss[sp->ply])) { - ss[sp->ply].reduction = OnePly; - value = -search(pos, ss, -(sp->beta-1), newDepth - OnePly, sp->ply+1, true, threadID); + double red = 0.5 + ln(moveCount) * ln(sp->depth / 2) / 3.0; + if (red >= 1.0) + { + ss[sp->ply].reduction = Depth(int(floor(red * int(OnePly)))); + value = -search(pos, ss, -(sp->beta-1), newDepth-ss[sp->ply].reduction, sp->ply+1, true, threadID); + } + else + value = sp->beta; // Just to trigger next condition } else value = sp->beta; // Just to trigger next condition @@ -1973,13 +2007,18 @@ namespace { // 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. if ( !dangerous - && moveCount >= LMRPVMoves && !captureOrPromotion && !move_is_castle(move) && !move_is_killer(move, ss[sp->ply])) { - ss[sp->ply].reduction = OnePly; - value = -search(pos, ss, -sp->alpha, newDepth - OnePly, sp->ply+1, true, threadID); + double red = 0.5 + ln(moveCount) * ln(sp->depth / 2) / 6.0; + if (red >= 1.0) + { + ss[sp->ply].reduction = Depth(int(floor(red * int(OnePly)))); + value = -search(pos, ss, -sp->alpha, newDepth-ss[sp->ply].reduction, sp->ply+1, true, threadID); + } + else + value = sp->alpha + 1; // Just to trigger next condition } else value = sp->alpha + 1; // Just to trigger next condition @@ -2474,6 +2513,23 @@ namespace { } + // refine_eval() returns the transposition table score if + // possible otherwise falls back on static position evaluation. + + Value refine_eval(const TTEntry* tte, Value defaultEval, int ply) { + + if (!tte) + return defaultEval; + + Value v = value_from_tt(tte->value(), ply); + + if ( (is_lower_bound(tte->type()) && v >= defaultEval) + || (is_upper_bound(tte->type()) && v < defaultEval)) + return v; + + return defaultEval; + } + // update_history() registers a good move that produced a beta-cutoff // in history and marks as failures all the other moves of that ply.