X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=be324c16edff44b08a7450edbd8d016cf65fe351;hp=3b81fa86dae57f154d2a41df79620c14a0e6bfb5;hb=c8773c720af6fd5c3a8f84550ee36e5eb89f929e;hpb=9b257ba29de2b0d50b6bdbd175509fdb3aaf8e91 diff --git a/src/search.cpp b/src/search.cpp index 3b81fa86..be324c16 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -152,6 +152,16 @@ namespace { // evaluation of the position is more than NullMoveMargin below beta. const Value NullMoveMargin = Value(0x300); + //Null move search refutes move when Nullvalue >= Beta - Delta. Index is depth + //in full plies. Last index is 9+. + const Value NullMoveDeltaMidgame[] = + { Value(-8), Value( 6), Value(-15), Value( 9), Value(21), + Value(34), Value(54), Value( 59), Value(61), Value(61) }; + + const Value NullMoveDeltaEndgame[] = + { Value( 6), Value( 0), Value(-13), Value(-9), Value(-35), + Value(12), Value(24), Value( 9), Value( 5), Value( 5) }; + // Pruning criterions. See the code and comments in ok_to_prune() to // understand their precise meaning. const bool PruneEscapeMoves = false; @@ -259,8 +269,6 @@ namespace { Depth depth, int ply, int threadID); void sp_search(SplitPoint *sp, int threadID); void sp_search_pv(SplitPoint *sp, int threadID); - void init_search_stack(SearchStack& ss); - void init_search_stack(SearchStack ss[]); void init_node(const Position &pos, SearchStack ss[], int ply, int threadID); void update_pv(SearchStack ss[], int ply); void sp_update_pv(SearchStack *pss, SearchStack ss[], int ply); @@ -290,8 +298,8 @@ namespace { bool thread_is_available(int slave, int master); bool idle_thread_exists(int master); bool split(const Position &pos, SearchStack *ss, int ply, - Value *alpha, Value *beta, Value *bestValue, Depth depth, - int *moves, MovePicker *mp, int master, bool pvNode); + Value *alpha, Value *beta, Value *bestValue, Depth depth, int *moves, + MovePicker *mp, Bitboard dcCandidates, int master, bool pvNode); void wake_sleeping_threads(); #if !defined(_MSC_VER) @@ -324,6 +332,24 @@ History H; // Should be made local? SearchStack EmptySearchStack; +// SearchStack::init() initializes a search stack. Used at the beginning of a +// new search from the root. +void SearchStack::init(int ply) { + + pv[ply] = pv[ply + 1] = MOVE_NONE; + currentMove = threatMove = MOVE_NONE; + reduction = Depth(0); + currentMoveCaptureValue = Value(0); +} + +void SearchStack::initKillers() { + + mateKiller = MOVE_NONE; + for (int i = 0; i < KILLER_MAX; i++) + killers[i] = MOVE_NONE; +} + + //// //// Functions //// @@ -588,7 +614,8 @@ void init_threads() { } // Init also the empty search stack - init_search_stack(EmptySearchStack); + EmptySearchStack.init(0); + EmptySearchStack.initKillers(); } @@ -640,8 +667,11 @@ namespace { // Initialize TT.new_search(); H.clear(); - init_search_stack(ss); - + for (int i = 0; i < 3; i++) + { + ss[i].init(i); + ss[i].initKillers(); + } ValueByIteration[0] = Value(0); ValueByIteration[1] = rml.get_move_score(0); Iteration = 1; @@ -753,12 +783,12 @@ namespace { if (dbg_show_hit_rate) dbg_print_hit_rate(LogFile); - UndoInfo u; + StateInfo st; LogFile << "Nodes: " << nodes_searched() << std::endl << "Nodes/second: " << nps() << std::endl << "Best move: " << move_to_san(p, ss[0].pv[0]) << std::endl; - p.do_move(ss[0].pv[0], u); + p.do_move(ss[0].pv[0], st); LogFile << "Ponder move: " << move_to_san(p, ss[0].pv[1]) << std::endl << std::endl; } @@ -775,13 +805,14 @@ namespace { Value alpha = -VALUE_INFINITE; Value beta = VALUE_INFINITE, value; + Bitboard dcCandidates = pos.discovered_check_candidates(pos.side_to_move()); // Loop through all the moves in the root move list for (int i = 0; i < rml.move_count() && !AbortSearch; i++) { int64_t nodes; Move move; - UndoInfo u; + StateInfo st; Depth ext, newDepth; RootMoveNumber = i + 1; @@ -807,7 +838,7 @@ namespace { newDepth = (Iteration - 2) * OnePly + ext + InitialDepth; // Make the move, and search it - pos.do_move(move, u); + pos.do_move(move, st, dcCandidates); if (i < MultiPV) { @@ -981,6 +1012,7 @@ namespace { Move move, movesSearched[256]; int moveCount = 0; Value value, bestValue = -VALUE_INFINITE; + Bitboard dcCandidates = mp.discovered_check_candidates(); Color us = pos.side_to_move(); bool isCheck = pos.is_check(); bool mateThreat = pos.has_mate_threat(opposite_color(us)); @@ -994,7 +1026,7 @@ namespace { assert(move_is_ok(move)); bool singleReply = (isCheck && mp.number_of_moves() == 1); - bool moveIsCheck = pos.move_is_check(move); + bool moveIsCheck = pos.move_is_check(move, dcCandidates); bool moveIsCapture = pos.move_is_capture(move); movesSearched[moveCount++] = ss[ply].currentMove = move; @@ -1011,8 +1043,8 @@ namespace { Depth newDepth = depth - OnePly + ext; // Make and search the move - UndoInfo u; - pos.do_move(move, u); + StateInfo st; + pos.do_move(move, st, dcCandidates); if (moveCount == 1) // The first move in list is the PV value = -search_pv(pos, ss, -beta, -alpha, newDepth, ply+1, threadID); @@ -1087,7 +1119,7 @@ namespace { && !AbortSearch && !thread_should_stop(threadID) && split(pos, ss, ply, &alpha, &beta, &bestValue, depth, - &moveCount, &mp, threadID, true)) + &moveCount, &mp, dcCandidates, threadID, true)) break; } @@ -1180,13 +1212,19 @@ namespace { && ok_to_do_nullmove(pos) && approximateEval >= beta - NullMoveMargin) { + //Calculate correct delta. Idea and tuning from Joona Kiiski. + ScaleFactor factor[2] = { SCALE_FACTOR_NORMAL, SCALE_FACTOR_NORMAL }; + Phase phase = pos.game_phase(); + int i = Min(depth / OnePly, 9); + Value delta = scale_by_game_phase(NullMoveDeltaMidgame[i], NullMoveDeltaEndgame[i], phase, factor); + ss[ply].currentMove = MOVE_NULL; - UndoInfo u; - pos.do_null_move(u); + StateInfo st; + pos.do_null_move(st); int R = (depth >= 4 * OnePly ? 4 : 3); // Null move dynamic reduction - Value nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, false, threadID); + Value nullValue = -search(pos, ss, -(beta-delta-1), depth-R*OnePly, ply+1, false, threadID); // Check for a null capture artifact, if the value without the null capture // is above beta then mark the node as a suspicious failed low. We will verify @@ -1201,13 +1239,13 @@ namespace { && pos.see(ss[ply + 1].currentMove) + nullValue >= beta) nullDrivenIID = true; - pos.undo_null_move(u); + pos.undo_null_move(); if (value_is_mate(nullValue)) { /* Do not return unproven mates */ } - else if (nullValue >= beta) + else if (nullValue >= beta - delta) { if (depth < 6 * OnePly) return beta; @@ -1281,6 +1319,7 @@ namespace { Move move, movesSearched[256]; int moveCount = 0; Value value, bestValue = -VALUE_INFINITE; + Bitboard dcCandidates = mp.discovered_check_candidates(); Value futilityValue = VALUE_NONE; bool useFutilityPruning = UseFutilityPruning && depth < SelectiveDepth @@ -1295,7 +1334,7 @@ namespace { assert(move_is_ok(move)); bool singleReply = (isCheck && mp.number_of_moves() == 1); - bool moveIsCheck = pos.move_is_check(move); + bool moveIsCheck = pos.move_is_check(move, dcCandidates); bool moveIsCapture = pos.move_is_capture(move); movesSearched[moveCount++] = ss[ply].currentMove = move; @@ -1334,8 +1373,8 @@ namespace { } // Make and search the move - UndoInfo u; - pos.do_move(move, u); + StateInfo st; + pos.do_move(move, st, dcCandidates); // 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. @@ -1382,7 +1421,7 @@ namespace { && !AbortSearch && !thread_should_stop(threadID) && split(pos, ss, ply, &beta, &beta, &bestValue, depth, &moveCount, - &mp, threadID, false)) + &mp, dcCandidates, threadID, false)) break; } @@ -1467,6 +1506,7 @@ namespace { MovePicker mp = MovePicker(pos, pvNode, MOVE_NONE, EmptySearchStack, depth, isCheck ? NULL : &ei); Move move; int moveCount = 0; + Bitboard dcCandidates = mp.discovered_check_candidates(); Color us = pos.side_to_move(); bool enoughMaterial = pos.non_pawn_material(us) > RookValueMidgame; @@ -1486,7 +1526,7 @@ namespace { && !isCheck && !pvNode && !move_promotion(move) - && !pos.move_is_check(move) + && !pos.move_is_check(move, dcCandidates) && !pos.move_is_passed_pawn_push(move)) { Value futilityValue = staticValue @@ -1513,8 +1553,8 @@ namespace { continue; // Make and search the move. - UndoInfo u; - pos.do_move(move, u); + StateInfo st; + pos.do_move(move, st, dcCandidates); Value value = -qsearch(pos, ss, -beta, -alpha, depth-OnePly, ply+1, threadID); pos.undo_move(move); @@ -1581,7 +1621,7 @@ namespace { { assert(move_is_ok(move)); - bool moveIsCheck = pos.move_is_check(move); + bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates); bool moveIsCapture = pos.move_is_capture(move); lock_grab(&(sp->lock)); @@ -1605,8 +1645,8 @@ namespace { continue; // Make and search the move. - UndoInfo u; - pos.do_move(move, u); + StateInfo st; + pos.do_move(move, st, sp->dcCandidates); // 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. @@ -1691,7 +1731,7 @@ namespace { && !thread_should_stop(threadID) && (move = sp->mp->get_next_move(sp->lock)) != MOVE_NONE) { - bool moveIsCheck = pos.move_is_check(move); + bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates); bool moveIsCapture = pos.move_is_capture(move); assert(move_is_ok(move)); @@ -1714,8 +1754,8 @@ namespace { Depth newDepth = sp->depth - OnePly + ext; // Make and search the move. - UndoInfo u; - pos.do_move(move, u); + StateInfo st; + pos.do_move(move, st, sp->dcCandidates); // 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. @@ -1876,12 +1916,12 @@ namespace { if (includeMove) { // Find a quick score for the move - UndoInfo u; + StateInfo st; SearchStack ss[PLY_MAX_PLUS_2]; moves[count].move = mlist[i].move; moves[count].nodes = 0ULL; - pos.do_move(moves[count].move, u); + pos.do_move(moves[count].move, st); moves[count].score = -qsearch(pos, ss, -VALUE_INFINITE, VALUE_INFINITE, Depth(0), 1, 0); pos.undo_move(moves[count].move); @@ -1986,34 +2026,6 @@ namespace { } - // init_search_stack() initializes a search stack at the beginning of a - // new search from the root. - void init_search_stack(SearchStack& ss) { - - ss.pv[0] = MOVE_NONE; - ss.pv[1] = MOVE_NONE; - ss.currentMove = MOVE_NONE; - ss.threatMove = MOVE_NONE; - ss.reduction = Depth(0); - for (int j = 0; j < KILLER_MAX; j++) - ss.killers[j] = MOVE_NONE; - } - - void init_search_stack(SearchStack ss[]) { - - for (int i = 0; i < 3; i++) - { - ss[i].pv[i] = MOVE_NONE; - ss[i].pv[i+1] = MOVE_NONE; - ss[i].currentMove = MOVE_NONE; - ss[i].threatMove = MOVE_NONE; - ss[i].reduction = Depth(0); - for (int j = 0; j < KILLER_MAX; j++) - ss[i].killers[j] = MOVE_NONE; - } - } - - // init_node() is called at the beginning of all the search functions // (search(), search_pv(), qsearch(), and so on) and initializes the search // stack object corresponding to the current node. Once every @@ -2033,13 +2045,9 @@ namespace { NodesSincePoll = 0; } } - ss[ply].pv[ply] = ss[ply].pv[ply+1] = ss[ply].currentMove = MOVE_NONE; - ss[ply+2].mateKiller = MOVE_NONE; - ss[ply].threatMove = MOVE_NONE; - ss[ply].reduction = Depth(0); - ss[ply].currentMoveCaptureValue = Value(0); - for (int j = 0; j < KILLER_MAX; j++) - ss[ply+2].killers[j] = MOVE_NONE; + + ss[ply].init(ply); + ss[ply+2].initKillers(); if(Threads[threadID].printCurrentLine) print_current_line(ss, ply, threadID); @@ -2671,8 +2679,9 @@ namespace { // splitPoint->cpus becomes 0), split() returns true. bool split(const Position &p, SearchStack *sstck, int ply, - Value *alpha, Value *beta, Value *bestValue, - Depth depth, int *moves, MovePicker *mp, int master, bool pvNode) { + Value *alpha, Value *beta, Value *bestValue, Depth depth, int *moves, + MovePicker *mp, Bitboard dcCandidates, int master, bool pvNode) { + assert(p.is_ok()); assert(sstck != NULL); assert(ply >= 0 && ply < PLY_MAX); @@ -2708,6 +2717,7 @@ namespace { splitPoint->alpha = pvNode? *alpha : (*beta - 1); splitPoint->beta = *beta; splitPoint->pvNode = pvNode; + splitPoint->dcCandidates = dcCandidates; splitPoint->bestValue = *bestValue; splitPoint->master = master; splitPoint->mp = mp;