X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=3b5cae4541eecec487b02bb3cb72c7233492e453;hp=359c99c66aba1df979d60c47f4436f02db076a58;hb=8ee0842c81ba41482d3373d4174bfed53b1bc9fc;hpb=977ca40d6d20f4c56d6ed4afe76ecf91da38e001 diff --git a/src/search.cpp b/src/search.cpp index 359c99c6..3b5cae45 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -28,7 +28,6 @@ #include #include -#include "bitcount.h" #include "book.h" #include "evaluate.h" #include "history.h" @@ -190,22 +189,9 @@ namespace { // Remaining depth: 1 ply 1.5 ply 2 ply 2.5 ply 3 ply 3.5 ply const Value RazorApprMargins[6] = { Value(0x520), Value(0x300), Value(0x300), Value(0x300), Value(0x300), Value(0x300) }; - // The main transposition table - TranspositionTable TT; - /// Variables initialized by UCI options - // Adjustable playing strength - int Slowdown = 0; - const int SlowdownArray[32] = { - 19, 41, 70, 110, 160, 230, 320, 430, 570, 756, 1000, 1300, 1690, 2197, - 2834, 3600, 4573, 5809, 7700, 9863, 12633, 16181, 20726, 26584, 34005, - 43557, 55792, 71463, 91536, 117247, 150180, 192363 - }; - int Strength; - const int MaxStrength = 25; - // Minimum number of full depth (i.e. non-reduced) moves at PV and non-PV nodes int LMRPVMoves, LMRNonPVMoves; // heavy SMP read access for the latter @@ -213,7 +199,7 @@ namespace { Depth ThreatDepth; // heavy SMP read access // Last seconds noise filtering (LSN) - const bool UseLSNFiltering = true; + const bool UseLSNFiltering = false; const int LSNTime = 4000; // In milliseconds const Value LSNValue = value_from_centipawns(200); bool loseOnTime = false; @@ -292,7 +278,7 @@ namespace { 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); - void init_node(const Position& pos, SearchStack ss[], int ply, int threadID); + void init_node(SearchStack ss[], int ply, int threadID); void update_pv(SearchStack ss[], int ply); void sp_update_pv(SearchStack* pss, SearchStack ss[], int ply); bool connected_moves(const Position& pos, Move m1, Move m2); @@ -305,7 +291,6 @@ namespace { bool ok_to_history(const Position& pos, Move m); void update_history(const Position& pos, Move m, Depth depth, Move movesSearched[], int moveCount); void update_killers(Move m, SearchStack& ss); - void slowdown(const Position& pos); bool fail_high_ply_1(); int current_search_time(); @@ -314,6 +299,7 @@ namespace { void ponderhit(); void print_current_line(SearchStack ss[], int ply, int threadID); void wait_for_stop_or_ponderhit(); + void init_ss_array(SearchStack ss[]); void idle_loop(int threadID, SplitPoint* waitSp); void init_split_point_stack(); @@ -426,10 +412,8 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move, read_weights(pos.side_to_move()); - // Set the number of active threads. If UCI_LimitStrength is enabled, never - // use more than one thread. - int newActiveThreads = - get_option_value_bool("UCI_LimitStrength")? 1 : get_option_value_int("Threads"); + // Set the number of active threads + int newActiveThreads = get_option_value_int("Threads"); if (newActiveThreads != ActiveThreads) { ActiveThreads = newActiveThreads; @@ -442,19 +426,6 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move, for (int i = 1; i < ActiveThreads; i++) assert(thread_is_available(i, 0)); - // Set playing strength - if (get_option_value_bool("UCI_LimitStrength")) - { - Strength = (get_option_value_int("UCI_Elo") - 2100) / 25; - Slowdown = - (Strength == MaxStrength)? 0 : SlowdownArray[Max(0, 31-Strength)]; - } - else - { - Strength = MaxStrength; - Slowdown = 0; - } - // Set thinking time int myTime = time[side_to_move]; int myIncrement = increment[side_to_move]; @@ -475,7 +446,8 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move, if (movesToGo == 1) { MaxSearchTime = myTime / 2; - AbsoluteMaxSearchTime = Min(myTime / 2, myTime - 500); + AbsoluteMaxSearchTime = + (myTime > 3000)? (myTime - 500) : ((myTime * 3) / 4); } else { MaxSearchTime = myTime / Min(movesToGo, 20); AbsoluteMaxSearchTime = Min((4 * myTime) / movesToGo, myTime / 3); @@ -499,13 +471,10 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move, NodesBetweenPolls = Min(MaxNodes, 30000); InfiniteSearch = true; // HACK } - else if (Slowdown) { - if (Slowdown > 50000) NodesBetweenPolls = 30; - else if (Slowdown > 10000) NodesBetweenPolls = 100; - else if (Slowdown > 1000) NodesBetweenPolls = 500; - else if (Slowdown > 100) NodesBetweenPolls = 3000; - else NodesBetweenPolls = 15000; - } + else if (myTime && myTime < 1000) + NodesBetweenPolls = 1000; + else if (myTime && myTime < 5000) + NodesBetweenPolls = 5000; else NodesBetweenPolls = 30000; @@ -526,9 +495,9 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move, { Value v = id_loop(pos, searchMoves); loseOnTime = ( UseLSNFiltering - && myTime < LSNTime - && myIncrement == 0 - && v < -LSNValue); + && myTime < LSNTime + && myIncrement == 0 + && v < -LSNValue); } else { @@ -661,14 +630,19 @@ namespace { // searchMoves are verified, copied, scored and sorted RootMoveList rml(p, searchMoves); + // Print RootMoveList c'tor startup scoring to the standard output, + // so that we print information also for iteration 1. + std::cout << "info depth " << 1 << "\ninfo depth " << 1 + << " score " << value_to_string(rml.get_move_score(0)) + << " time " << current_search_time() + << " nodes " << nodes_searched() + << " nps " << nps() + << " pv " << rml.get_move(0) << "\n"; + // Initialize TT.new_search(); H.clear(); - for (int i = 0; i < 3; i++) - { - ss[i].init(i); - ss[i].initKillers(); - } + init_ss_array(ss); IterationInfo[1] = IterationInfoType(rml.get_move_score(0), rml.get_move_score(0)); Iteration = 1; @@ -894,7 +868,7 @@ namespace { // Decide search depth for this move bool moveIsCapture = pos.move_is_capture(move); bool dangerous; - ext = extension(pos, move, true, pos.move_is_capture(move), pos.move_is_check(move), false, false, &dangerous); + ext = extension(pos, move, true, moveIsCapture, pos.move_is_check(move), false, false, &dangerous); newDepth = (Iteration - 2) * OnePly + ext + InitialDepth; // Make the move, and search it @@ -918,8 +892,8 @@ namespace { } else { - if (newDepth >= 3*OnePly - && i + MultiPV >= LMRPVMoves + if ( newDepth >= 3*OnePly + && i >= MultiPV + LMRPVMoves && !dangerous && !moveIsCapture && !move_is_promotion(move) @@ -927,10 +901,10 @@ namespace { { ss[0].reduction = OnePly; value = -search(pos, ss, -alpha, newDepth-OnePly, 1, true, 0); - } - else + } else value = alpha + 1; // Just to trigger next condition - if(value > alpha) + + if (value > alpha) { value = -search(pos, ss, -alpha, newDepth, 1, true, 0); if (value > alpha) @@ -975,7 +949,7 @@ namespace { // Update PV rml.set_move_score(i, value); update_pv(ss, 0); - TT.extract_pv(pos, ss[0].pv); + TT.extract_pv(pos, ss[0].pv, PLY_MAX); rml.set_move_pv(i, ss[0].pv); if (MultiPV == 1) @@ -1002,7 +976,10 @@ namespace { std::cout << std::endl; if (UseLogFile) - LogFile << pretty_pv(pos, current_search_time(), Iteration, nodes_searched(), value, ss[0].pv) + LogFile << pretty_pv(pos, current_search_time(), Iteration, nodes_searched(), value, + ((value >= beta)? VALUE_TYPE_LOWER + : ((value <= alpha)? VALUE_TYPE_UPPER : VALUE_TYPE_EXACT)), + ss[0].pv) << std::endl; if (value > alpha) @@ -1059,7 +1036,7 @@ namespace { // Initialize, and make an early exit in case of an aborted search, // an instant draw, maximum ply reached, etc. - init_node(pos, ss, ply, threadID); + init_node(ss, ply, threadID); // After init_node() that calls poll() if (AbortSearch || thread_should_stop(threadID)) @@ -1094,16 +1071,16 @@ namespace { // Initialize a MovePicker object for the current position, and prepare // to search all moves - MovePicker mp = MovePicker(pos, ttMove, depth, H, &ss[ply]); - 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)); + MovePicker mp = MovePicker(pos, ttMove, depth, H, &ss[ply]); + Bitboard dcCandidates = mp.discovered_check_candidates(); + // Loop through all legal moves until no moves remain or a beta cutoff // occurs. while ( alpha < beta @@ -1112,7 +1089,7 @@ namespace { { assert(move_is_ok(move)); - bool singleReply = (isCheck && mp.number_of_moves() == 1); + bool singleReply = (isCheck && mp.number_of_evasions() == 1); bool moveIsCheck = pos.move_is_check(move, dcCandidates); bool moveIsCapture = pos.move_is_capture(move); @@ -1249,7 +1226,7 @@ namespace { // Initialize, and make an early exit in case of an aborted search, // an instant draw, maximum ply reached, etc. - init_node(pos, ss, ply, threadID); + init_node(ss, ply, threadID); // After init_node() that calls poll() if (AbortSearch || thread_should_stop(threadID)) @@ -1361,6 +1338,10 @@ namespace { bool useFutilityPruning = depth < SelectiveDepth && !isCheck; + // 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) + FutilityMargins[int(depth) - 2]; + // Loop through all legal moves until no moves remain or a beta cutoff // occurs. while ( bestValue < beta @@ -1369,7 +1350,7 @@ namespace { { assert(move_is_ok(move)); - bool singleReply = (isCheck && mp.number_of_moves() == 1); + bool singleReply = (isCheck && mp.number_of_evasions() == 1); bool moveIsCheck = pos.move_is_check(move, dcCandidates); bool moveIsCapture = pos.move_is_capture(move); @@ -1505,7 +1486,7 @@ namespace { // Initialize, and make an early exit in case of an aborted search, // an instant draw, maximum ply reached, etc. - init_node(pos, ss, ply, threadID); + init_node(ss, ply, threadID); // After init_node() that calls poll() if (AbortSearch || thread_should_stop(threadID)) @@ -1538,7 +1519,7 @@ namespace { if (isCheck) staticValue = -VALUE_INFINITE; - else if (tte && tte->type() == VALUE_TYPE_EVAL) + else if (tte && (tte->type() & VALUE_TYPE_EVAL)) { // Use the cached evaluation score if possible assert(ei.futilityMargin == Value(0)); @@ -1559,7 +1540,7 @@ namespace { { // Store the score to avoid a future costly evaluation() call if (!isCheck && !tte && ei.futilityMargin == 0) - TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_EVAL, Depth(-127*OnePly), MOVE_NONE); + TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_EV_LO, Depth(-127*OnePly), MOVE_NONE); return bestValue; } @@ -1647,9 +1628,13 @@ namespace { Move m = ss[ply].pv[ply]; if (!pvNode) { + // 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. + ValueType type = (bestValue == staticValue && !ei.futilityMargin ? VALUE_TYPE_EV_UP : VALUE_TYPE_UPPER); Depth d = (depth == Depth(0) ? Depth(0) : Depth(-1)); + if (bestValue < beta) - TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_UPPER, d, MOVE_NONE); + 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, m); } @@ -1965,15 +1950,15 @@ namespace { bool includeAllMoves = (searchMoves[0] == MOVE_NONE); // Generate all legal moves - int lm_count = generate_legal_moves(pos, mlist); + MoveStack* last = generate_moves(pos, mlist); // Add each move to the moves[] array - for (int i = 0; i < lm_count; i++) + for (MoveStack* cur = mlist; cur != last; cur++) { bool includeMove = includeAllMoves; for (int k = 0; !includeMove && searchMoves[k] != MOVE_NONE; k++) - includeMove = (searchMoves[k] == mlist[i].move); + includeMove = (searchMoves[k] == cur->move); if (!includeMove) continue; @@ -1981,8 +1966,9 @@ namespace { // Find a quick score for the move StateInfo st; SearchStack ss[PLY_MAX_PLUS_2]; + init_ss_array(ss); - moves[count].move = mlist[i].move; + moves[count].move = cur->move; 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); @@ -2092,14 +2078,11 @@ namespace { // NodesBetweenPolls nodes, init_node() also calls poll(), which polls // for user input and checks whether it is time to stop the search. - void init_node(const Position& pos, SearchStack ss[], int ply, int threadID) { + void init_node(SearchStack ss[], int ply, int threadID) { assert(ply >= 0 && ply < PLY_MAX); assert(threadID >= 0 && threadID < ActiveThreads); - if (Slowdown && Iteration >= 3) - slowdown(pos); - Threads[threadID].nodes++; if (threadID == 0) @@ -2156,7 +2139,9 @@ namespace { // the second move is assumed to be a move from the current position. bool connected_moves(const Position& pos, Move m1, Move m2) { + Square f1, t1, f2, t2; + Piece p; assert(move_is_ok(m1)); assert(move_is_ok(m2)); @@ -2182,31 +2167,32 @@ namespace { return true; // Case 4: The destination square for m2 is attacked by the moving piece in m1 - if (pos.piece_attacks_square(pos.piece_on(t1), t1, t2)) + p = pos.piece_on(t1); + if (bit_is_set(pos.attacks_from(p, t1), t2)) return true; // Case 5: Discovered check, checking piece is the piece moved in m1 - if ( piece_is_slider(pos.piece_on(t1)) + if ( piece_is_slider(p) && bit_is_set(squares_between(t1, pos.king_square(pos.side_to_move())), f2) - && !bit_is_set(squares_between(t2, pos.king_square(pos.side_to_move())), t2)) + && !bit_is_set(squares_between(t1, pos.king_square(pos.side_to_move())), t2)) { Bitboard occ = pos.occupied_squares(); Color us = pos.side_to_move(); Square ksq = pos.king_square(us); clear_bit(&occ, f2); - if (pos.type_of_piece_on(t1) == BISHOP) + if (type_of_piece(p) == BISHOP) { if (bit_is_set(bishop_attacks_bb(ksq, occ), t1)) return true; } - else if (pos.type_of_piece_on(t1) == ROOK) + else if (type_of_piece(p) == ROOK) { if (bit_is_set(rook_attacks_bb(ksq, occ), t1)) return true; } else { - assert(pos.type_of_piece_on(t1) == QUEEN); + assert(type_of_piece(p) == QUEEN); if (bit_is_set(queen_attacks_bb(ksq, occ), t1)) return true; } @@ -2270,12 +2256,13 @@ namespace { if (pos.type_of_piece_on(move_from(m)) == PAWN) { - if (pos.move_is_pawn_push_to_7th(m)) + Color c = pos.side_to_move(); + if (relative_rank(c, move_to(m)) == RANK_7) { result += PawnPushTo7thExtension[pvNode]; *dangerous = true; } - if (pos.move_is_passed_pawn_push(m)) + if (pos.pawn_is_passed(c, move_to(m))) { result += PassedPawnExtension[pvNode]; *dangerous = true; @@ -2433,20 +2420,6 @@ namespace { } - // slowdown() simply wastes CPU cycles doing nothing useful. It's used - // in strength handicap mode. - - void slowdown(const Position &pos) { - int i, n; - n = Slowdown; - for (i = 0; i < n; i++) { - Square s = Square(i&63); - if (count_1s(pos.attacks_to(s)) > 63) - std::cout << "This can't happen, but I put this string here anyway, in order to prevent the compiler from optimizing away the useless computation." << std::endl; - } - } - - // fail_high_ply_1() checks if some thread is currently resolving a fail // high at ply 1 at the node below the first root node. This information // is used for time managment. @@ -2593,6 +2566,18 @@ namespace { } + // init_ss_array() does a fast reset of the first entries of a SearchStack array + + void init_ss_array(SearchStack ss[]) { + + for (int i = 0; i < 3; i++) + { + ss[i].init(i); + ss[i].initKillers(); + } + } + + // wait_for_stop_or_ponderhit() is called when the maximum depth is reached // while the program is pondering. The point is to work around a wrinkle in // the UCI protocol: When pondering, the engine is not allowed to give a