From 5c4002aa827653a125130a0d01d0bb96dd2b8bae Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 2 May 2019 19:22:29 +0200 Subject: [PATCH 01/54] Simplified shuffle extension version (#2121) only the extension part of the shuffle patch is sufficient to pass [0,3.5] bounds at VLTC as shown by two more tests. http://tests.stockfishchess.org/tests/view/5cc168bc0ebc5925cf02bda8 LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 120684 W: 17875 L: 17400 D: 85409 http://tests.stockfishchess.org/tests/view/5cc14d510ebc5925cf02bcb5 LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 68415 W: 10250 L: 9905 D: 48260 this patch proposes to simplify back to this basic and easier to understand version. In case there is a need to run a [-3, 1] VLTC on this one, it can be done, but it is resource intensive, and not needed IMO. Bench: 3388643 --- src/search.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 517499b5..44347f79 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -606,15 +606,6 @@ namespace { : ttHit ? tte->move() : MOVE_NONE; ttPv = (ttHit && tte->is_pv()) || (PvNode && depth > 4 * ONE_PLY); - // If position has been searched at higher depths and we are shuffling, - // return value_draw. - if ( pos.rule50_count() > 36 - 6 * (pos.count() > 14) - && ss->ply > 36 - 6 * (pos.count() > 14) - && ttHit - && tte->depth() > depth - && pos.count() > 0) - return VALUE_DRAW; - // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ttHit @@ -939,9 +930,8 @@ moves_loop: // When in check, search starts from here // Shuffle extension else if ( PvNode && pos.rule50_count() > 18 - && ss->ply > 18 && depth < 3 * ONE_PLY - && ss->ply < 3 * thisThread->rootDepth / ONE_PLY) // To avoid infinite loops + && ss->ply < 3 * thisThread->rootDepth / ONE_PLY) // To avoid too deep searches extension = ONE_PLY; // Passed pawn extension From 4e72e2a964754611de85536c13ae069f85839b85 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sun, 31 Mar 2019 12:02:19 +0200 Subject: [PATCH 02/54] Assorted trivial cleanups 4/2019 No functional change. --- src/bitbase.cpp | 3 ++- src/bitboard.cpp | 10 ++-------- src/bitboard.h | 14 ++++++++++---- src/evaluate.cpp | 24 +++++++++++------------- src/misc.cpp | 6 ------ src/misc.h | 1 - src/pawns.cpp | 7 ++++--- src/pawns.h | 2 +- src/position.cpp | 6 +++--- src/position.h | 6 +++--- src/search.cpp | 7 ++++--- src/thread.cpp | 1 + src/timeman.cpp | 1 + src/ucioption.cpp | 1 + 14 files changed, 43 insertions(+), 46 deletions(-) diff --git a/src/bitbase.cpp b/src/bitbase.cpp index 8c8c4702..2b1a5517 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -27,7 +27,8 @@ namespace { - // There are 24 possible pawn squares: the first 4 files and ranks from 2 to 7 + // There are 24 possible pawn squares: files A to D and ranks from 2 to 7. + // Positions with the pawn on files E to H will be mirrored before probing. constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608 // Each uint32_t stores results of 32 positions, one per bit diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 0ab244c7..281579c4 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -18,8 +18,8 @@ along with this program. If not, see . */ -#include #include +#include #include "bitboard.h" #include "misc.h" @@ -27,16 +27,10 @@ uint8_t PopCnt16[1 << 16]; uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; +Bitboard SquareBB[SQUARE_NB]; Bitboard LineBB[SQUARE_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; -Bitboard SquareBB[SQUARE_NB]; - -Bitboard KingFlank[FILE_NB] = { - QueenSide ^ FileDBB, QueenSide, QueenSide, - CenterFiles, CenterFiles, - KingSide, KingSide, KingSide ^ FileEBB -}; Magic RookMagics[SQUARE_NB]; Magic BishopMagics[SQUARE_NB]; diff --git a/src/bitboard.h b/src/bitboard.h index 53e96f44..4e0267f1 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -65,14 +65,19 @@ constexpr Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB; constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB; constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB); +constexpr Bitboard KingFlank[FILE_NB] = { + QueenSide ^ FileDBB, QueenSide, QueenSide, + CenterFiles, CenterFiles, + KingSide, KingSide, KingSide ^ FileEBB +}; + extern uint8_t PopCnt16[1 << 16]; extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; +extern Bitboard SquareBB[SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; -extern Bitboard KingFlank[FILE_NB]; -extern Bitboard SquareBB[SQUARE_NB]; /// Magic holds all magic bitboards relevant data for a single square @@ -148,6 +153,7 @@ inline Bitboard file_bb(Square s) { template constexpr Bitboard shift(Bitboard b) { return D == NORTH ? b << 8 : D == SOUTH ? b >> 8 + : D == NORTH+NORTH? b <<16 : D == SOUTH+SOUTH? b >>16 : D == EAST ? (b & ~FileHBB) << 1 : D == WEST ? (b & ~FileABB) >> 1 : D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7 : D == SOUTH_EAST ? (b & ~FileHBB) >> 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9 @@ -370,8 +376,8 @@ inline Square pop_lsb(Bitboard* b) { } -/// frontmost_sq() and backmost_sq() return the square corresponding to the -/// most/least advanced bit relative to the given color. +/// frontmost_sq() and backmost_sq() return the most/least advanced square in +/// the given bitboard relative to the given color. inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); } inline Square backmost_sq(Color c, Bitboard b) { return c == WHITE ? lsb(b) : msb(b); } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 7e6260c8..5750d82a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -18,6 +18,7 @@ along with this program. If not, see . */ +#include #include #include // For std::memset #include @@ -186,9 +187,8 @@ namespace { // is also calculated is ALL_PIECES. Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB]; - // attackedBy2[color] are the squares attacked by 2 pieces of a given color, - // possibly via x-ray or by one pawn and one piece. Diagonal x-ray through - // pawn or squares attacked by 2 pawns are not explicitly added. + // attackedBy2[color] are the squares attacked by at least 2 units of a given + // color, including x-rays. But diagonal x-rays through pawns are not computed. Bitboard attackedBy2[COLOR_NB]; // kingRing[color] are the squares adjacent to the king, plus (only for a @@ -241,8 +241,7 @@ namespace { attackedBy[Us][KING] = pos.attacks_from(ksq); attackedBy[Us][PAWN] = pe->pawn_attacks(Us); attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN]; - attackedBy2[Us] = (attackedBy[Us][KING] & attackedBy[Us][PAWN]) - | dblAttackByPawn; + attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]); // Init our king safety tables kingRing[Us] = attackedBy[Us][KING]; @@ -309,11 +308,11 @@ namespace { bb = OutpostRanks & ~pe->pawn_attacks_span(Them); if (bb & s) score += Outpost * (Pt == KNIGHT ? 4 : 2) - * (1 + bool(attackedBy[Us][PAWN] & s)); + * ((attackedBy[Us][PAWN] & s) ? 2 : 1); else if (bb &= b & ~pos.pieces(Us)) score += Outpost * (Pt == KNIGHT ? 2 : 1) - * (1 + bool(attackedBy[Us][PAWN] & bb)); + * ((attackedBy[Us][PAWN] & bb) ? 2 : 1); // Knight and Bishop bonus for being right behind a pawn if (shift(pos.pieces(PAWN)) & s) @@ -358,8 +357,8 @@ namespace { score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]); // Bonus for rook on an open or semi-open file - if (pos.semiopen_file(Us, file_of(s))) - score += RookOnFile[bool(pos.semiopen_file(Them, file_of(s)))]; + if (pos.is_semiopen_file(Us, file_of(s))) + score += RookOnFile[bool(pos.is_semiopen_file(Them, file_of(s)))]; // Penalty when trapped by the king, even more if the king cannot castle else if (mob <= 3) @@ -672,7 +671,7 @@ namespace { bonus += make_score(k * w, k * w); } - } // rank > RANK_3 + } // r > RANK_3 // Scale down bonus for candidate passers which need more than one // pawn push to become passed, or have a pawn in front of them. @@ -717,7 +716,7 @@ namespace { // Find all squares which are at most three squares behind some friendly pawn Bitboard behind = pos.pieces(Us, PAWN); behind |= shift(behind); - behind |= shift(shift(behind)); + behind |= shift(behind); int bonus = popcount(safe) + popcount(behind & safe); int weight = pos.count(Us) @@ -777,8 +776,7 @@ namespace { if (sf == SCALE_FACTOR_NORMAL) { if ( pos.opposite_bishops() - && pos.non_pawn_material(WHITE) == BishopValueMg - && pos.non_pawn_material(BLACK) == BishopValueMg) + && pos.non_pawn_material() == 2 * BishopValueMg) sf = 16 + 4 * pe->passed_count(); else sf = std::min(40 + (pos.opposite_bishops() ? 2 : 7) * pos.count(strongSide), sf); diff --git a/src/misc.cpp b/src/misc.cpp index 449e07ce..8d3b202d 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -210,12 +210,6 @@ void prefetch(void* addr) { #endif -void prefetch2(void* addr) { - - prefetch(addr); - prefetch((uint8_t*)addr + 64); -} - namespace WinProcGroup { #ifndef _WIN32 diff --git a/src/misc.h b/src/misc.h index 4b238df5..ddd05e4e 100644 --- a/src/misc.h +++ b/src/misc.h @@ -31,7 +31,6 @@ const std::string engine_info(bool to_uci = false); void prefetch(void* addr); -void prefetch2(void* addr); void start_logger(const std::string& fname); void dbg_hit_on(bool b); diff --git a/src/pawns.cpp b/src/pawns.cpp index 095126d4..0a88c9f1 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -18,6 +18,7 @@ along with this program. If not, see . */ +#include #include #include "bitboard.h" @@ -175,14 +176,14 @@ Value Entry::evaluate_shelter(const Position& pos, Square ksq) { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH); - constexpr Bitboard BlockRanks = (Us == WHITE ? Rank1BB | Rank2BB : Rank8BB | Rank7BB); + constexpr Bitboard BlockSquares = (Rank1BB | Rank2BB | Rank7BB | Rank8BB) + & (FileABB | FileHBB); Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq); Bitboard ourPawns = b & pos.pieces(Us); Bitboard theirPawns = b & pos.pieces(Them); - Value safety = (shift(theirPawns) & (FileABB | FileHBB) & BlockRanks & ksq) ? - Value(374) : Value(5); + Value safety = (shift(theirPawns) & BlockSquares & ksq) ? Value(374) : Value(5); File center = clamp(file_of(ksq), FILE_B, FILE_G); for (File f = File(center - 1); f <= File(center + 1); ++f) diff --git a/src/pawns.h b/src/pawns.h index 88b55545..76c5ffc9 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -38,7 +38,7 @@ struct Entry { Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } int weak_unopposed(Color c) const { return weakUnopposed[c]; } - int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); }; + int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); } template Score king_safety(const Position& pos) { diff --git a/src/position.cpp b/src/position.cpp index ada03fbc..8c6dc802 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -18,6 +18,7 @@ along with this program. If not, see . */ +#include #include #include // For offsetof() #include // For std::memset, std::memcmp @@ -340,8 +341,8 @@ void Position::set_castling_right(Color c, Square rfrom) { Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1); Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1); - castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto) - & ~(square_bb(kfrom) | rfrom); + castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto) + & ~(square_bb(kfrom) | rfrom); } @@ -859,7 +860,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Update pawn hash key and prefetch access to pawnsTable st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; - prefetch2(thisThread->pawnsTable[st->pawnKey]); // Reset rule 50 draw counter st->rule50 = 0; diff --git a/src/position.h b/src/position.h index 1078e03e..a3fb16b8 100644 --- a/src/position.h +++ b/src/position.h @@ -95,7 +95,7 @@ public: template int count() const; template const Square* squares(Color c) const; template Square square(Color c) const; - int semiopen_file(Color c, File f) const; + bool is_semiopen_file(Color c, File f) const; // Castling int castling_rights(Color c) const; @@ -262,7 +262,7 @@ inline Square Position::ep_square() const { return st->epSquare; } -inline int Position::semiopen_file(Color c, File f) const { +inline bool Position::is_semiopen_file(Color c, File f) const { return !(pieces(c, PAWN) & file_bb(f)); } @@ -321,7 +321,7 @@ inline bool Position::pawn_passed(Color c, Square s) const { inline bool Position::advanced_pawn_push(Move m) const { return type_of(moved_piece(m)) == PAWN - && relative_rank(sideToMove, from_sq(m)) > RANK_4; + && relative_rank(sideToMove, to_sq(m)) > RANK_5; } inline int Position::pawns_on_same_color_squares(Color c, Square s) const { diff --git a/src/search.cpp b/src/search.cpp index 44347f79..082be4f2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -18,6 +18,7 @@ along with this program. If not, see . */ +#include #include #include #include // For std::memset @@ -894,7 +895,7 @@ moves_loop: // When in check, search starts from here && move == ttMove && !rootNode && !excludedMove // Avoid recursive singular search - /* && ttValue != VALUE_NONE Already implicit in the next condition */ + /* && ttValue != VALUE_NONE Already implicit in the next condition */ && abs(ttValue) < VALUE_KNOWN_WIN && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3 * ONE_PLY @@ -1201,8 +1202,8 @@ moves_loop: // When in check, search starts from here } - // qsearch() is the quiescence search function, which is called by the main - // search function with depth zero, or recursively with depth less than ONE_PLY. + // qsearch() is the quiescence search function, which is called by the main search + // function with zero depth, or recursively with further decreasing depth per call. template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { diff --git a/src/thread.cpp b/src/thread.cpp index f216c321..2f1237a3 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -20,6 +20,7 @@ #include +#include // For std::count #include "movegen.h" #include "search.h" #include "thread.h" diff --git a/src/timeman.cpp b/src/timeman.cpp index fafde2aa..484aaa65 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -18,6 +18,7 @@ along with this program. If not, see . */ +#include #include #include diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 54f33c9a..813a0890 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -18,6 +18,7 @@ along with this program. If not, see . */ +#include #include #include #include From aba906b734e03f90c7f32587cc845c927483aac8 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Sat, 4 May 2019 07:41:53 -0400 Subject: [PATCH 03/54] Stat Score reset at rootNode - Bench: 3393330 (#2124) At rootNode reset great great grandchildren stat score i.e (ss + 4)->statScore = 0 STC: (yellow) LLR: -2.96 (-2.94,2.94) [0.50,4.50] Total: 256079 W: 57423 L: 56315 D: 142341 http://tests.stockfishchess.org/tests/view/5ccb0c420ebc5925cf03a6a5 LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 61550 W: 10611 L: 10260 D: 40679 http://tests.stockfishchess.org/tests/view/5ccbf9d00ebc5925cf03c487 Bench: 3393330 --- src/search.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 082be4f2..36e73a09 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -594,7 +594,10 @@ namespace { // starts with statScore = 0. Later grandchildren start with the last calculated // statScore of the previous grandchild. This influences the reduction rules in // LMR which are based on the statScore of parent position. - (ss+2)->statScore = 0; + if (rootNode) + (ss + 4)->statScore = 0; + else + (ss + 2)->statScore = 0; // Step 4. Transposition table lookup. We don't want the score of a partial // search to overwrite a previous full search TT value, so we use a different From b6d11028bbb5e428cdbd709ba46d8b14bab17c88 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 5 May 2019 15:58:52 +0200 Subject: [PATCH 04/54] LMR for captures not cracking alpha Enable LMR for a capture/promotion move which does not seem to have a good chance to fail high according to static eval and value of captured piece. STC: LLR: 2.96 (-2.94,2.94) [0.50,4.50] Total: 40477 W: 9158 L: 8792 D: 22527 http://tests.stockfishchess.org/tests/view/5cceedc60ebc5925cf04174f LTC: LLR: 2.94 (-2.94,2.94) [0.00,3.50] Total: 21926 W: 3873 L: 3634 D: 14419 http://tests.stockfishchess.org/tests/view/5ccf04310ebc5925cf041ab0 Bench: 3644175 --- src/search.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 36e73a09..bafc374b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1008,7 +1008,9 @@ moves_loop: // When in check, search starts from here // re-searched at full depth. if ( depth >= 3 * ONE_PLY && moveCount > 1 - && (!captureOrPromotion || moveCountPruning)) + && ( !captureOrPromotion + || moveCountPruning + || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha)) { Depth r = reduction(improving, depth, moveCount); From 368f976fb6fc9096d7f19b0443fb4d1af40eec14 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Wed, 8 May 2019 21:40:46 +0200 Subject: [PATCH 05/54] Less LMR at root Do no LMR for the first four moves if at root node. STC: LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 19686 W: 4524 L: 4261 D: 10901 http://tests.stockfishchess.org/tests/view/5cd3577b0ebc5925cf04a089 LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 88335 W: 15193 L: 14766 D: 58376 http://tests.stockfishchess.org/tests/view/5cd35e600ebc5925cf04a1c3 Bench: 3184182 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index bafc374b..866444a8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1007,7 +1007,7 @@ moves_loop: // When in check, search starts from here // Step 16. Reduced depth search (LMR). If the move fails high it will be // re-searched at full depth. if ( depth >= 3 * ONE_PLY - && moveCount > 1 + && moveCount > 1 + 3 * rootNode && ( !captureOrPromotion || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha)) From ad8b78ad5281138cfe188dedcf76ec8ef622531f Mon Sep 17 00:00:00 2001 From: Sergei Ivanov Date: Sun, 14 Apr 2019 15:50:37 +0300 Subject: [PATCH 06/54] Fix cycle detection in presence of repetitions In master search() may incorrectly return a draw score in the following corner case: there was a 2-fold repetition during the game, and the current position can be reached by a move from a repeated one. This case is treated as an upcoming 3-fold repetition, which it is not. Here is a testcase demonstrating the issue (note that the moves after FEN are required). The input: position fen 8/8/8/8/8/8/p7/2k4K b - - 0 1 moves c1b1 h1g1 b1c1 g1h1 c1b1 h1g1 b1a1 g1h1 go movetime 1000 produces the output: [...] info depth 127 seldepth 2 multipv 1 score cp 0 [...] bestmove a1b1 saying that the game will be drawn by repetion. However the other possible move for black, Kb2, avoids repetitions and wins. The patch fixes this behavior. In particular it finds mate in 10 in the above position. STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 10604 W: 2390 L: 2247 D: 5967 http://tests.stockfishchess.org/tests/view/5cb373e00ebc5925cf0167bf LTC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 19620 W: 3308 L: 3185 D: 13127 http://tests.stockfishchess.org/tests/view/5cb3822f0ebc5925cf016b2d Bench is not changed since it does not test positions with history of moves. Bench: 3184182 --- src/position.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/position.cpp b/src/position.cpp index 8c6dc802..a86d77ad 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1206,6 +1206,11 @@ bool Position::has_game_cycle(int ply) const { if (ply > i) return true; + // For nodes before or at the root, check that the move is a repetition one + // rather than a move to the current position + if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move()) + continue; + // For repetitions before or at the root, require one more StateInfo* next_stp = stp; for (int k = i + 2; k <= end; k += 2) From 8a0af1004ae898f1f7a36a00705548cc255bec28 Mon Sep 17 00:00:00 2001 From: Miguel Lahoz Date: Tue, 7 May 2019 23:55:56 +0800 Subject: [PATCH 07/54] Remove PvNode template from reduction This functional simplification removes the PvNode reduction and adjusts the ttPv lmr condition accordingly. Their definitions only differ by the inclusions of ttPv. Aside from this, shallow move pruning definition will be the only other functional difference, but this does not seem to matter too much. STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 58908 W: 12980 L: 12932 D: 32996 http://tests.stockfishchess.org/tests/view/5cd1aaaa0ebc5925cf046c6a LTC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 20351 W: 3521 L: 3399 D: 13431 http://tests.stockfishchess.org/tests/view/5cd23fa70ebc5925cf047cd2 Bench: 3687854 --- src/search.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 866444a8..5d6babb4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -70,9 +70,9 @@ namespace { // Reductions lookup table, initialized at startup int Reductions[MAX_MOVES]; // [depth or moveNumber] - template Depth reduction(bool i, Depth d, int mn) { + Depth reduction(bool i, Depth d, int mn) { int r = Reductions[d / ONE_PLY] * Reductions[mn] / 1024; - return ((r + 512) / 1024 + (!i && r > 1024) - PvNode) * ONE_PLY; + return ((r + 512) / 1024 + (!i && r > 1024)) * ONE_PLY; } constexpr int futility_move_count(bool improving, int depth) { @@ -964,7 +964,7 @@ moves_loop: // When in check, search starts from here continue; // Reduced depth of the next LMR search - int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO); + int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO); lmrDepth /= ONE_PLY; // Countermoves based pruning (~20 Elo) @@ -1012,11 +1012,11 @@ moves_loop: // When in check, search starts from here || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha)) { - Depth r = reduction(improving, depth, moveCount); + Depth r = reduction(improving, depth, moveCount); // Decrease reduction if position is or has been on the PV if (ttPv) - r -= ONE_PLY; + r -= 2 * ONE_PLY; // Decrease reduction if opponent's move count is high (~10 Elo) if ((ss-1)->moveCount > 15) From 7df832fea63761775494d7e360102c62230005c7 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 15 May 2019 10:49:29 +0300 Subject: [PATCH 08/54] Decrease reduction in case we had singular extension. #2146 Passed STC http://tests.stockfishchess.org/tests/view/5cda71790ebc5925cf057a84 LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 73454 W: 16482 L: 15954 D: 41018 Passed LTC http://tests.stockfishchess.org/tests/view/5cdab17b0ebc5925cf05822f LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 56696 W: 9877 L: 9538 D: 37281 Original idea by @locutus2 bench 3378510 --- src/search.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 5d6babb4..f021f8ff 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -855,6 +855,7 @@ moves_loop: // When in check, search starts from here value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc moveCountPruning = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); + int singularExtensionLMRmultiplier = 0; // Step 12. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. @@ -911,7 +912,12 @@ moves_loop: // When in check, search starts from here ss->excludedMove = MOVE_NONE; if (value < singularBeta) + { extension = ONE_PLY; + singularExtensionLMRmultiplier++; + if (value < singularBeta - std::min(3 * depth / ONE_PLY, 39)) + singularExtensionLMRmultiplier++; + } // Multi-cut pruning // Our ttMove is assumed to fail high, and now we failed high also on a reduced @@ -1021,6 +1027,8 @@ moves_loop: // When in check, search starts from here // Decrease reduction if opponent's move count is high (~10 Elo) if ((ss-1)->moveCount > 15) r -= ONE_PLY; + // Decrease reduction if move has been singularly extended + r -= singularExtensionLMRmultiplier * ONE_PLY; if (!captureOrPromotion) { From 893a08a8c2eac3c2e7d2216b51811cc7378e2239 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 15 May 2019 09:52:27 +0200 Subject: [PATCH 09/54] Allow for higher depths. (#2147) High rootDepths, selDepths and generally searches are increasingly common with long time control games, analysis, and improving hardware. In this case, depths of MAX_DEPTH/MAX_PLY (128) can be reached, and the search tree is truncated. In principle MAX_PLY can be easily increased, except for a technicality of storing depths in a signed 8 bit int in the TT. This patch increases MAX_PLY by storing the depth in an unsigned 8 bit, after shifting by the most negative depth stored in TT (DEPTH_NONE). No regression at STC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 42235 W: 9565 L: 9484 D: 23186 http://tests.stockfishchess.org/tests/view/5cdb35360ebc5925cf0595e1 Verified to reach high depths on k1b5/1p1p4/pP1Pp3/K2pPp2/1P1p1P2/3P1P2/5P2/8 w - - info depth 142 seldepth 154 multipv 1 score cp 537 nodes 26740713110 ... No bench change. --- src/tt.cpp | 5 +++-- src/tt.h | 4 ++-- src/types.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index 33768ca4..b8fe7567 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -43,14 +43,15 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) // Overwrite less valuable entries if ( (k >> 48) != key16 - || d / ONE_PLY > depth8 - 4 + || d / ONE_PLY + 10 > depth8 || b == BOUND_EXACT) { key16 = (uint16_t)(k >> 48); value16 = (int16_t)v; eval16 = (int16_t)ev; genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); - depth8 = (int8_t)(d / ONE_PLY); + assert((d - DEPTH_NONE) / ONE_PLY >= 0); + depth8 = (uint8_t)((d - DEPTH_NONE) / ONE_PLY); } } diff --git a/src/tt.h b/src/tt.h index fefc8e2c..3608b77c 100644 --- a/src/tt.h +++ b/src/tt.h @@ -40,7 +40,7 @@ struct TTEntry { Move move() const { return (Move )move16; } Value value() const { return (Value)value16; } Value eval() const { return (Value)eval16; } - Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)); } + Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)) + DEPTH_NONE; } bool is_pv() const { return (bool)(genBound8 & 0x4); } Bound bound() const { return (Bound)(genBound8 & 0x3); } void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev); @@ -53,7 +53,7 @@ private: int16_t value16; int16_t eval16; uint8_t genBound8; - int8_t depth8; + uint8_t depth8; }; diff --git a/src/types.h b/src/types.h index 2e36279d..b0758f43 100644 --- a/src/types.h +++ b/src/types.h @@ -101,7 +101,7 @@ typedef uint64_t Key; typedef uint64_t Bitboard; constexpr int MAX_MOVES = 256; -constexpr int MAX_PLY = 128; +constexpr int MAX_PLY = 246; /// A move needs 16 bits to be stored /// From 5f4d44fda053335ab20cb67c99323b8c182bf824 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sun, 12 May 2019 22:20:51 +0100 Subject: [PATCH 10/54] Add eg component to evaluate_shelter() #2137 Add an endgame component to the blockedstorm penalty so that the penalty applies more uniformly through the game. STC 10+0.1 th 1 : LLR: -2.95 (-2.94,2.94) [0.50,4.50] Total: 94063 W: 21426 L: 21118 D: 51519 http://tests.stockfishchess.org/tests/view/5cd4605c0ebc5925cf04bf43 LTC 60+0.6 th 1 : LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 188232 W: 32808 L: 32090 D: 123334 http://tests.stockfishchess.org/tests/view/5cd47d0a0ebc5925cf04c4fd Refactored code with higher constant values gave a more convincing LTC result: LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 30050 W: 5330 L: 5066 D: 19654 http://tests.stockfishchess.org/tests/view/5cd6a0000ebc5925cf050653 Bench: 3687700 --- src/pawns.cpp | 26 ++++++++++++++++---------- src/pawns.h | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 0a88c9f1..8d6f812a 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -172,7 +172,7 @@ Entry* probe(const Position& pos) { /// penalty for a king, looking at the king file and the two closest files. template -Value Entry::evaluate_shelter(const Position& pos, Square ksq) { +void Entry::evaluate_shelter(const Position& pos, Square ksq, Score& shelter) { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH); @@ -183,7 +183,8 @@ Value Entry::evaluate_shelter(const Position& pos, Square ksq) { Bitboard ourPawns = b & pos.pieces(Us); Bitboard theirPawns = b & pos.pieces(Them); - Value safety = (shift(theirPawns) & BlockSquares & ksq) ? Value(374) : Value(5); + Value bonus[] = { (shift(theirPawns) & BlockSquares & ksq) ? Value(374) : Value(5), + VALUE_ZERO }; File center = clamp(file_of(ksq), FILE_B, FILE_G); for (File f = File(center - 1); f <= File(center + 1); ++f) @@ -195,12 +196,16 @@ Value Entry::evaluate_shelter(const Position& pos, Square ksq) { Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; int d = std::min(f, ~f); - safety += ShelterStrength[d][ourRank]; - safety -= (ourRank && (ourRank == theirRank - 1)) ? 66 * (theirRank == RANK_3) - : UnblockedStorm[d][theirRank]; + bonus[MG] += ShelterStrength[d][ourRank]; + + if (ourRank && (ourRank == theirRank - 1)) + bonus[MG] -= 82 * (theirRank == RANK_3), bonus[EG] -= 82 * (theirRank == RANK_3); + else + bonus[MG] -= UnblockedStorm[d][theirRank]; } - return safety; + if (bonus[MG] > mg_value(shelter)) + shelter = make_score(bonus[MG], bonus[EG]); } @@ -223,16 +228,17 @@ Score Entry::do_king_safety(const Position& pos) { else while (pawns) minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns))); - Value bonus = evaluate_shelter(pos, ksq); + Score shelter = make_score(-VALUE_INFINITE, VALUE_ZERO); + evaluate_shelter(pos, ksq, shelter); // If we can castle use the bonus after the castling if it is bigger if (pos.can_castle(Us | KING_SIDE)) - bonus = std::max(bonus, evaluate_shelter(pos, relative_square(Us, SQ_G1))); + evaluate_shelter(pos, relative_square(Us, SQ_G1), shelter); if (pos.can_castle(Us | QUEEN_SIDE)) - bonus = std::max(bonus, evaluate_shelter(pos, relative_square(Us, SQ_C1))); + evaluate_shelter(pos, relative_square(Us, SQ_C1), shelter); - return make_score(bonus, -16 * minPawnDist); + return shelter - make_score(VALUE_ZERO, 16 * minPawnDist); } // Explicit template instantiation diff --git a/src/pawns.h b/src/pawns.h index 76c5ffc9..771e9cd3 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -50,7 +50,7 @@ struct Entry { Score do_king_safety(const Position& pos); template - Value evaluate_shelter(const Position& pos, Square ksq); + void evaluate_shelter(const Position& pos, Square ksq, Score& shelter); Key key; Score scores[COLOR_NB]; From a8abba0b4d127f608e8e8dde4583d6389b3c2339 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sun, 5 May 2019 14:22:40 +0100 Subject: [PATCH 11/54] Remove pawn count in space() calculation #2139 Simplification. Various attempts to optimise the pawn count bonus showed little effect, so remove pawn count altogether and compensate by subtracting 1 instead of 4. STC 10+0.1 th 1: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 152244 W: 33709 L: 33847 D: 84688 http://tests.stockfishchess.org/tests/view/5cceed330ebc5925cf04170e LTC 60+0.6 th 1: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 24100 W: 4079 L: 3964 D: 16057 http://tests.stockfishchess.org/tests/view/5cd5b6b80ebc5925cf04e889 Bench: 3648841 --- src/evaluate.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 5750d82a..d1a8ffd3 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -719,9 +719,7 @@ namespace { behind |= shift(behind); int bonus = popcount(safe) + popcount(behind & safe); - int weight = pos.count(Us) - - (16 - pos.count()) / 4; - + int weight = pos.count(Us) - 1; Score score = make_score(bonus * weight * weight / 16, 0); if (T) From 9c7dc057d160d7f3c41553173f958f2cc7b5d98c Mon Sep 17 00:00:00 2001 From: svivanov72 <48968697+svivanov72@users.noreply.github.com> Date: Wed, 15 May 2019 11:22:21 +0300 Subject: [PATCH 12/54] Precompute repetition info (#2132) Store repetition info in StateInfo instead of recomputing it in three different places. This saves some work in has_game_cycle() where this info is needed for positions before the root. Tested for non-regression at STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 34104 W: 7586 L: 7489 D: 19029 http://tests.stockfishchess.org/tests/view/5cd0676e0ebc5925cf044b56 No functional change. --- src/position.cpp | 73 +++++++++++++++++++++--------------------------- src/position.h | 1 + 2 files changed, 33 insertions(+), 41 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index a86d77ad..901e91a7 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -879,6 +879,25 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Update king attacks used for fast check detection set_check_info(st); + // Calculate the repetition info. It is the ply distance from the previous + // occurrence of the same position, negative in the 3-fold case, or zero + // if the position was not repeated. + st->repetition = 0; + int end = std::min(st->rule50, st->pliesFromNull); + if (end >= 4) + { + StateInfo* stp = st->previous->previous; + for (int i=4; i <= end; i += 2) + { + stp = stp->previous->previous; + if (stp->key == st->key) + { + st->repetition = stp->repetition ? -i : i; + break; + } + } + } + assert(pos_is_ok()); } @@ -994,6 +1013,8 @@ void Position::do_null_move(StateInfo& newSt) { set_check_info(st); + st->repetition = 0; + assert(pos_is_ok()); } @@ -1117,24 +1138,10 @@ bool Position::is_draw(int ply) const { if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) return true; - int end = std::min(st->rule50, st->pliesFromNull); - - if (end < 4) - return false; - - StateInfo* stp = st->previous->previous; - int cnt = 0; - - for (int i = 4; i <= end; i += 2) - { - stp = stp->previous->previous; - - // Return a draw score if a position repeats once earlier but strictly - // after the root, or repeats twice before or at the root. - if ( stp->key == st->key - && ++cnt + (ply > i) == 2) - return true; - } + // Return a draw score if a position repeats once earlier but strictly + // after the root, or repeats twice before or at the root. + if (st->repetition && st->repetition < ply) + return true; return false; } @@ -1146,26 +1153,15 @@ bool Position::is_draw(int ply) const { bool Position::has_repeated() const { StateInfo* stc = st; - while (true) + int end = std::min(st->rule50, st->pliesFromNull); + while (end-- >= 4) { - int i = 4, end = std::min(stc->rule50, stc->pliesFromNull); - - if (end < i) - return false; - - StateInfo* stp = stc->previous->previous; - - do { - stp = stp->previous->previous; - - if (stp->key == stc->key) - return true; - - i += 2; - } while (i <= end); + if (stc->repetition) + return true; stc = stc->previous; } + return false; } @@ -1212,13 +1208,8 @@ bool Position::has_game_cycle(int ply) const { continue; // For repetitions before or at the root, require one more - StateInfo* next_stp = stp; - for (int k = i + 2; k <= end; k += 2) - { - next_stp = next_stp->previous->previous; - if (next_stp->key == stp->key) - return true; - } + if (stp->repetition) + return true; } } } diff --git a/src/position.h b/src/position.h index a3fb16b8..5ff3d1ac 100644 --- a/src/position.h +++ b/src/position.h @@ -46,6 +46,7 @@ struct StateInfo { Square epSquare; // Not copied when making a move (will be recomputed anyhow) + int repetition; Key key; Bitboard checkersBB; Piece capturedPiece; From 66820a2668a7892a0eb49bedc0ed2c3b4baa74a7 Mon Sep 17 00:00:00 2001 From: protonspring Date: Wed, 15 May 2019 02:24:00 -0600 Subject: [PATCH 13/54] Simplify Thread Voting Scheme #2129 This is a functional simplification of the math in the voting scheme. It took a bit longer to pass LTC 8 threads, so perhaps more testing is needed at longer times and/or more threads. STC 4 threads LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 22315 W: 4852 L: 4732 D: 12731 http://tests.stockfishchess.org/tests/view/5ccc86280ebc5925cf03d439 STC 8 threads LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 42427 W: 8451 L: 8369 D: 25607 http://tests.stockfishchess.org/tests/view/5cccb67c0ebc5925cf03da90 LTC 4 Threads LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 23513 W: 4208 L: 4092 D: 15213 http://tests.stockfishchess.org/tests/view/5ccce94d0ebc5925cf03e1ec LTC 8 Threads LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 70098 W: 11442 L: 11399 D: 47257 http://tests.stockfishchess.org/tests/view/5ccd22aa0ebc5925cf03e463 No functional change (in single thread) --- src/search.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f021f8ff..e57131eb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -241,10 +241,8 @@ void MainThread::search() { // Vote according to score and depth for (Thread* th : Threads) - { - int64_t s = th->rootMoves[0].score - minScore + 1; - votes[th->rootMoves[0].pv[0]] += 200 + s * s * int(th->completedDepth); - } + votes[th->rootMoves[0].pv[0]] += + (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); // Select best thread auto bestVote = votes[this->rootMoves[0].pv[0]]; From 2d9fac1e13ce724b6d5fafdf03d880ca37055dde Mon Sep 17 00:00:00 2001 From: Moez Jellouli <37274752+MJZ1977@users.noreply.github.com> Date: Wed, 15 May 2019 10:26:32 +0200 Subject: [PATCH 14/54] Simplify reduction formula #2122 Simplify reduction formula No functional change. --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e57131eb..0e9674a0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -149,7 +149,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int(1024 * std::log(i) / std::sqrt(1.95)); + Reductions[i] = int(733.3 * std::log(i)); } From 4fcd78bd06d3c1936f6fac9b26fe6d8c57c7747e Mon Sep 17 00:00:00 2001 From: protonspring Date: Tue, 23 Apr 2019 12:26:58 -0600 Subject: [PATCH 15/54] Simplify connected #2114 This is a functional simplification that simplifies some of the math for connected pawns. The bench is different because I moved a /2 from opposed into the connected array. STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 37954 W: 8504 L: 8415 D: 21035 http://tests.stockfishchess.org/tests/view/5cbf599a0ebc5925cf028156 LTC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 27780 W: 4682 L: 4572 D: 18526 http://tests.stockfishchess.org/tests/view/5cbf6a5e0ebc5925cf0284b8 Bench 3824325 --- src/pawns.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 8d6f812a..291d40b6 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -37,7 +37,7 @@ namespace { constexpr Score Isolated = S( 5, 15); // Connected pawn bonus - constexpr int Connected[RANK_NB] = { 0, 13, 17, 24, 59, 96, 171 }; + constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; // Strength of pawn shelter for our king by [distance from edge][rank]. // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. @@ -126,8 +126,9 @@ namespace { // Score this pawn if (support | phalanx) { - int v = (phalanx ? 3 : 2) * Connected[r]; - v = 17 * popcount(support) + (v >> (opposed + 1)); + int v = Connected[r] * (phalanx ? 3 : 2) / (opposed ? 2 : 1) + + 17 * popcount(support); + score += make_score(v, v * (r - 2) / 4); } else if (!neighbours) From 44c320a572188b5875291103edb344c584b91d19 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 15 May 2019 10:35:58 +0200 Subject: [PATCH 16/54] Make rootDepth local to search. (#2077) passed STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 61869 W: 13668 L: 13626 D: 34575 http://tests.stockfishchess.org/tests/view/5ca660eb0ebc5925cf004f0c No functional change. --- src/search.cpp | 2 +- src/thread.cpp | 2 +- src/thread.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 0e9674a0..b31157e4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -283,7 +283,7 @@ void Thread::search() { Move pv[MAX_PLY+1]; Value bestValue, alpha, beta, delta; Move lastBestMove = MOVE_NONE; - Depth lastBestMoveDepth = DEPTH_ZERO; + Depth lastBestMoveDepth = DEPTH_ZERO, rootDepth = DEPTH_ZERO; MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); double timeReduction = 1, totBestMoveChanges = 0; Color us = rootPos.side_to_move(); diff --git a/src/thread.cpp b/src/thread.cpp index 2f1237a3..d360ad31 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -192,7 +192,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, for (Thread* th : *this) { th->nodes = th->tbHits = th->nmpMinPly = 0; - th->rootDepth = th->completedDepth = DEPTH_ZERO; + th->completedDepth = DEPTH_ZERO; th->rootMoves = rootMoves; th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); } diff --git a/src/thread.h b/src/thread.h index 7a27aab1..fb11344c 100644 --- a/src/thread.h +++ b/src/thread.h @@ -67,7 +67,7 @@ public: Position rootPos; Search::RootMoves rootMoves; - Depth rootDepth, completedDepth; + Depth completedDepth; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; CapturePieceToHistory captureHistory; From 4a7b8180ecaef7d164fa53a1d545372df1173596 Mon Sep 17 00:00:00 2001 From: mstembera Date: Wed, 15 May 2019 01:41:58 -0700 Subject: [PATCH 17/54] Remove per thread instances of Endgames. (#2056) Similar to PSQT we only need one instance of the Endgames resource. The current per thread copies are identical and read only(after initialization) so from a design point of view it doesn't make sense to have them. Tested for no slowdown. http://tests.stockfishchess.org/tests/view/5c94377a0ebc5925cfff43ca LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 17320 W: 3487 L: 3359 D: 10474 No functional change. --- src/endgame.cpp | 27 +++++++++++++++++++++++++++ src/endgame.h | 33 ++++++--------------------------- src/main.cpp | 2 ++ src/material.cpp | 4 ++-- src/thread.h | 1 - 5 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 5958e633..7c4efa3c 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -82,6 +82,33 @@ namespace { } // namespace +namespace Endgames { + + std::pair, Map> maps; + + void init() { + + add("KPK"); + add("KNNK"); + add("KBNK"); + add("KRKP"); + add("KRKB"); + add("KRKN"); + add("KQKP"); + add("KQKR"); + add("KNNKP"); + + add("KNPK"); + add("KNPKB"); + add("KRPKR"); + add("KRPKB"); + add("KBPKB"); + add("KBPKN"); + add("KBPPKB"); + add("KRPPKRP"); + } +} + /// Mate with KX vs K. This function is used to evaluate positions with /// king and plenty of material vs a lone king. It simply gives the /// attacking side a bonus for driving the defending king towards the edge diff --git a/src/endgame.h b/src/endgame.h index 2a48488f..81afb2e5 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -95,10 +95,12 @@ struct Endgame : public EndgameBase { /// base objects in two std::map. We use polymorphism to invoke the actual /// endgame function by calling its virtual operator(). -class Endgames { +namespace Endgames { template using Ptr = std::unique_ptr>; template using Map = std::map>; + + extern std::pair, Map> maps; template Map& map() { @@ -113,35 +115,12 @@ class Endgames { map()[Position().set(code, BLACK, &st).material_key()] = Ptr(new Endgame(BLACK)); } - std::pair, Map> maps; - -public: - Endgames() { - - add("KPK"); - add("KNNK"); - add("KBNK"); - add("KRKP"); - add("KRKB"); - add("KRKN"); - add("KQKP"); - add("KQKR"); - add("KNNKP"); - - add("KNPK"); - add("KNPKB"); - add("KRPKR"); - add("KRPKB"); - add("KBPKB"); - add("KBPKN"); - add("KBPPKB"); - add("KRPPKRP"); - } - template const EndgameBase* probe(Key key) { return map().count(key) ? map()[key].get() : nullptr; } -}; + + void init(); +} #endif // #ifndef ENDGAME_H_INCLUDED diff --git a/src/main.cpp b/src/main.cpp index fc78b38c..57656f62 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,6 +26,7 @@ #include "thread.h" #include "tt.h" #include "uci.h" +#include "endgame.h" #include "syzygy/tbprobe.h" namespace PSQT { @@ -42,6 +43,7 @@ int main(int argc, char* argv[]) { Position::init(); Bitbases::init(); Search::init(); + Endgames::init(); Threads.set(Options["Threads"]); Search::clear(); // After threads are up diff --git a/src/material.cpp b/src/material.cpp index ee5d4bce..3a05f3fa 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -137,7 +137,7 @@ Entry* probe(const Position& pos) { // Let's look if we have a specialized evaluation function for this particular // material configuration. Firstly we look for a fixed configuration one, then // for a generic one if the previous search failed. - if ((e->evaluationFunction = pos.this_thread()->endgames.probe(key)) != nullptr) + if ((e->evaluationFunction = Endgames::probe(key)) != nullptr) return e; for (Color c = WHITE; c <= BLACK; ++c) @@ -149,7 +149,7 @@ Entry* probe(const Position& pos) { // OK, we didn't find any special evaluation function for the current material // configuration. Is there a suitable specialized scaling function? - const auto* sf = pos.this_thread()->endgames.probe(key); + const auto* sf = Endgames::probe(key); if (sf) { diff --git a/src/thread.h b/src/thread.h index fb11344c..0866d55d 100644 --- a/src/thread.h +++ b/src/thread.h @@ -59,7 +59,6 @@ public: Pawns::Table pawnsTable; Material::Table materialTable; - Endgames endgames; size_t pvIdx, pvLast; int selDepth, nmpMinPly; Color nmpColor; From 02708a4a1153d59c058f8056bc31713322130392 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Wed, 15 May 2019 10:50:27 +0200 Subject: [PATCH 18/54] Revert "Make rootDepth local to search. (#2077)" This reverts commit 44c320a572188b5875291103edb344c584b91d19. Fix a compile error. Bench: 3824325 --- src/search.cpp | 2 +- src/thread.cpp | 2 +- src/thread.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b31157e4..0e9674a0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -283,7 +283,7 @@ void Thread::search() { Move pv[MAX_PLY+1]; Value bestValue, alpha, beta, delta; Move lastBestMove = MOVE_NONE; - Depth lastBestMoveDepth = DEPTH_ZERO, rootDepth = DEPTH_ZERO; + Depth lastBestMoveDepth = DEPTH_ZERO; MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); double timeReduction = 1, totBestMoveChanges = 0; Color us = rootPos.side_to_move(); diff --git a/src/thread.cpp b/src/thread.cpp index d360ad31..2f1237a3 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -192,7 +192,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, for (Thread* th : *this) { th->nodes = th->tbHits = th->nmpMinPly = 0; - th->completedDepth = DEPTH_ZERO; + th->rootDepth = th->completedDepth = DEPTH_ZERO; th->rootMoves = rootMoves; th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); } diff --git a/src/thread.h b/src/thread.h index 0866d55d..114769d2 100644 --- a/src/thread.h +++ b/src/thread.h @@ -66,7 +66,7 @@ public: Position rootPos; Search::RootMoves rootMoves; - Depth completedDepth; + Depth rootDepth, completedDepth; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; CapturePieceToHistory captureHistory; From 3a572ffb4840bfea3c587c9c81a0008515f02a32 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sun, 31 Mar 2019 16:33:32 +0100 Subject: [PATCH 19/54] Update failedHighCnt rule #2063 Treat all threads the same as main thread and increment failedHighCnt on fail highs. This makes the search try again at lower depth. @vondele suggested also changing the reset of failedHighCnt when there is a fail low. Tests including this passed so the branch has been updated to include both changes. failedHighCnt is now handled exactly the same in helper threads and the main thread. Thanks vondele :-) STC @ 5+0.05 th 4 : LLR: 2.94 (-2.94,2.94) [-3.00,1.00] Total: 7769 W: 1704 L: 1557 D: 4508 http://tests.stockfishchess.org/tests/view/5c9f19520ebc5925cfffd2a1 LTC @ 20+0.2 th 8 : LLR: 2.94 (-2.94,2.94) [-3.00,1.00] Total: 37888 W: 5983 L: 5889 D: 26016 http://tests.stockfishchess.org/tests/view/5c9f57d10ebc5925cfffd696 Bench 3824325 --- src/search.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 0e9674a0..fdef1400 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -404,17 +404,14 @@ void Thread::search() { beta = (alpha + beta) / 2; alpha = std::max(bestValue - delta, -VALUE_INFINITE); + failedHighCnt = 0; if (mainThread) - { - failedHighCnt = 0; mainThread->stopOnPonderhit = false; - } } else if (bestValue >= beta) { beta = std::min(bestValue + delta, VALUE_INFINITE); - if (mainThread) - ++failedHighCnt; + ++failedHighCnt; } else break; From 3d076a0c50cae378c463fdd2a65a250eda55edab Mon Sep 17 00:00:00 2001 From: protonspring Date: Thu, 16 May 2019 06:11:00 -0600 Subject: [PATCH 20/54] Consolidate some code in set_state. (#2151) Non functional change. --- src/position.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 901e91a7..b8d7b106 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -383,6 +383,12 @@ void Position::set_state(StateInfo* si) const { Square s = pop_lsb(&b); Piece pc = piece_on(s); si->key ^= Zobrist::psq[pc][s]; + + if (type_of(pc) == PAWN) + si->pawnKey ^= Zobrist::psq[pc][s]; + + else if (type_of(pc) != PAWN && type_of(pc) != KING) + si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc]; } if (si->epSquare != SQ_NONE) @@ -393,20 +399,9 @@ void Position::set_state(StateInfo* si) const { si->key ^= Zobrist::castling[si->castlingRights]; - for (Bitboard b = pieces(PAWN); b; ) - { - Square s = pop_lsb(&b); - si->pawnKey ^= Zobrist::psq[piece_on(s)][s]; - } - for (Piece pc : Pieces) - { - if (type_of(pc) != PAWN && type_of(pc) != KING) - si->nonPawnMaterial[color_of(pc)] += pieceCount[pc] * PieceValue[MG][pc]; - for (int cnt = 0; cnt < pieceCount[pc]; ++cnt) si->materialKey ^= Zobrist::psq[pc][cnt]; - } } From 272936eaba8d3a40f4d2d6649fb9a06912bd162c Mon Sep 17 00:00:00 2001 From: protonspring Date: Thu, 16 May 2019 06:13:16 -0600 Subject: [PATCH 21/54] Score and Select Best Thread in same loop (#2125) This is a non-functional simplification that combines vote counting and thread selecting in the same loop. It is possible that the best thread would be updated more frequently than master, but I'm not sure it matters here. Perhaps "mostVotes" is a better name than "bestVote?" STC (stopped early). LLR: 0.70 (-2.94,2.94) [-3.00,1.00] Total: 10714 W: 2329 L: 2311 D: 6074 http://tests.stockfishchess.org/tests/view/5ccc71470ebc5925cf03d244 No functional change. --- src/search.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index fdef1400..f1136e3e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -239,19 +239,19 @@ void MainThread::search() { for (Thread* th: Threads) minScore = std::min(minScore, th->rootMoves[0].score); - // Vote according to score and depth + // Vote according to score and depth, and select the best thread + int64_t bestVote = 0; for (Thread* th : Threads) + { votes[th->rootMoves[0].pv[0]] += (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); - // Select best thread - auto bestVote = votes[this->rootMoves[0].pv[0]]; - for (Thread* th : Threads) if (votes[th->rootMoves[0].pv[0]] > bestVote) { bestVote = votes[th->rootMoves[0].pv[0]]; bestThread = th; } + } } previousScore = bestThread->rootMoves[0].score; From 2985a6b5d7be8b813da528886ff11265bfac449b Mon Sep 17 00:00:00 2001 From: svivanov72 <48968697+svivanov72@users.noreply.github.com> Date: Thu, 16 May 2019 15:14:11 +0300 Subject: [PATCH 22/54] Remove unused code (#2150) Remove an unused operator in has_game_cycle (thanks @vondele) and modify its comment to explain other code. No functional change. --- src/position.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index b8d7b106..edb40499 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1189,16 +1189,13 @@ bool Position::has_game_cycle(int ply) const { if (!(between_bb(s1, s2) & pieces())) { - // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in the same - // location. We select the legal one by reversing the move variable if necessary. - if (empty(s1)) - move = make_move(s2, s1); - if (ply > i) return true; // For nodes before or at the root, check that the move is a repetition one - // rather than a move to the current position + // rather than a move to the current position. + // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in the same + // location, so we have to select which square to check. if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move()) continue; From bf6b647a1afd0bfa25ee627622824a3ecb666c5d Mon Sep 17 00:00:00 2001 From: 31m059 <37052095+31m059@users.noreply.github.com> Date: Fri, 17 May 2019 07:38:23 -0400 Subject: [PATCH 23/54] Allow RQ through pieces. Bench: 3415326 (#2153) We evaluate defended and unsafe squares for a passed pawn push based on friendly and enemy rooks and queens on the passed pawn's file. Prior to this patch, we further required that these rooks and queens be able to directly attack the passed pawn. However, this restriction appears unnecessary and worth almost exactly 0 Elo at LTC. The simplified code allows rooks and queens to attack/defend the passed pawn through other pieces of either color. STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 29019 W: 6488 L: 6381 D: 16150 http://tests.stockfishchess.org/tests/view/5cdcf7270ebc5925cf05d30c LTC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 54224 W: 9200 L: 9133 D: 35891 http://tests.stockfishchess.org/tests/view/5cddc6210ebc5925cf05eca3 Bench: 3415326 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d1a8ffd3..dff84df3 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -649,7 +649,7 @@ namespace { // in the pawn's path attacked or occupied by the enemy. defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s); - bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from(s); + bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN); if (!(pos.pieces(Us) & bb)) defendedSquares &= attackedBy[Us][ALL_PIECES]; From 190f38a7c2a64f7e89f06871533d87ba7b287fdc Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 25 May 2019 09:43:52 +0200 Subject: [PATCH 24/54] Remove one division. (#2158) Can be included in the earlier calculation, with a small rounding difference. passed STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 17912 W: 4044 L: 3915 D: 9953 http://tests.stockfishchess.org/tests/view/5ce711f90ebc5925cf070d0e passed LTC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 56061 W: 9579 L: 9516 D: 36966 http://tests.stockfishchess.org/tests/view/5ce716820ebc5925cf070e37 Bench: 3817662 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f1136e3e..a3ce4c2d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -71,7 +71,7 @@ namespace { int Reductions[MAX_MOVES]; // [depth or moveNumber] Depth reduction(bool i, Depth d, int mn) { - int r = Reductions[d / ONE_PLY] * Reductions[mn] / 1024; + int r = Reductions[d / ONE_PLY] * Reductions[mn]; return ((r + 512) / 1024 + (!i && r > 1024)) * ONE_PLY; } @@ -149,7 +149,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int(733.3 * std::log(i)); + Reductions[i] = int(22.9 * std::log(i)); } From c645587270c2b08d327d290d0ae3c3f5e0a30eee Mon Sep 17 00:00:00 2001 From: protonspring Date: Wed, 29 May 2019 02:00:32 -0600 Subject: [PATCH 25/54] Simplify semiopen_file (#2165) This is a non-functional simplification. Since our file_bb handles either Files or Squares, using Square here removes some code. Not likely any performance difference despite the test. STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 6081 W: 1444 L: 1291 D: 3346 http://tests.stockfishchess.org/tests/view/5ceb3e2e0ebc5925cf07ab03 Non functional change. --- src/evaluate.cpp | 4 ++-- src/position.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index dff84df3..86f73563 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -357,8 +357,8 @@ namespace { score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]); // Bonus for rook on an open or semi-open file - if (pos.is_semiopen_file(Us, file_of(s))) - score += RookOnFile[bool(pos.is_semiopen_file(Them, file_of(s)))]; + if (pos.is_on_semiopen_file(Us, s)) + score += RookOnFile[bool(pos.is_on_semiopen_file(Them, s))]; // Penalty when trapped by the king, even more if the king cannot castle else if (mob <= 3) diff --git a/src/position.h b/src/position.h index 5ff3d1ac..343751ed 100644 --- a/src/position.h +++ b/src/position.h @@ -96,7 +96,7 @@ public: template int count() const; template const Square* squares(Color c) const; template Square square(Color c) const; - bool is_semiopen_file(Color c, File f) const; + bool is_on_semiopen_file(Color c, Square s) const; // Castling int castling_rights(Color c) const; @@ -263,8 +263,8 @@ inline Square Position::ep_square() const { return st->epSquare; } -inline bool Position::is_semiopen_file(Color c, File f) const { - return !(pieces(c, PAWN) & file_bb(f)); +inline bool Position::is_on_semiopen_file(Color c, Square s) const { + return !(pieces(c, PAWN) & file_bb(s)); } inline bool Position::can_castle(CastlingRight cr) const { From 3edf0e6b37df9c81ea1a4cd3affd11548547815b Mon Sep 17 00:00:00 2001 From: protonspring Date: Fri, 31 May 2019 06:35:39 -0600 Subject: [PATCH 26/54] Scale lazy threshold according to material. (#2170) STC LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 58543 W: 13238 L: 12782 D: 32523 http://tests.stockfishchess.org/tests/view/5cef3efa0ebc5925cf081f07 LTC LLR: 3.70 (-2.94,2.94) [0.00,3.50] Total: 82232 W: 14281 L: 13825 D: 54126 http://tests.stockfishchess.org/tests/view/5cef595d0ebc5925cf082441 bench 3807737 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 86f73563..d211db64 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -74,7 +74,7 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold = Value(1500); + constexpr Value LazyThreshold = Value(1400); constexpr Value SpaceThreshold = Value(12222); // KingAttackWeights[PieceType] contains king attack weights by piece type @@ -813,7 +813,7 @@ namespace { // Early exit if score is high Value v = (mg_value(score) + eg_value(score)) / 2; - if (abs(v) > LazyThreshold) + if (abs(v) > (LazyThreshold + pos.non_pawn_material() / 64)) return pos.side_to_move() == WHITE ? v : -v; // Main evaluation begins here From 434b2c72a44ba255c14957f519e3993ea3d5d6bc Mon Sep 17 00:00:00 2001 From: 31m059 <37052095+31m059@users.noreply.github.com> Date: Sun, 9 Jun 2019 08:19:07 -0400 Subject: [PATCH 27/54] Simplify k-value for passers. Bench: 3854907 (#2182) Stockfish evaluates passed pawns in part based on a variable k, which shapes the passed pawn bonus based on the number of squares between the current square and promotion square that are attacked by enemy pieces, and the number defended by friendly ones. Prior to this commit, we gave a large bonus when all squares between the pawn and the promotion square were defended, and if they were not, a somewhat smaller bonus if at least the pawn's next square was. However, this distinction does not appear to provide any Elo at STC or LTC. Where do we go from here? Many promising Elo-gaining patches were attempted in the past few months to refine passed pawn calculation, by altering the definitions of unsafe and defended squares. Stockfish uses these definitions to choose the value of k, so those tests interact with this PR. Therefore, it may be worthwhile to retest previously promising but not-quite-passing tests in the vicinity of this patch. STC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 42344 W: 9455 L: 9374 D: 23515 http://tests.stockfishchess.org/tests/view/5cf83ede0ebc5925cf0904fb LTC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 69548 W: 11855 L: 11813 D: 45880 http://tests.stockfishchess.org/tests/view/5cf8698f0ebc5925cf0908c8 Bench: 3854907 --- src/evaluate.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d211db64..8c1b0b08 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -661,13 +661,9 @@ namespace { // assign a smaller bonus if the block square isn't attacked. int k = !unsafeSquares ? 20 : !(unsafeSquares & blockSq) ? 9 : 0; - // If the path to the queen is fully defended, assign a big bonus. - // Otherwise assign a smaller bonus if the block square is defended. - if (defendedSquares == squaresToQueen) - k += 6; - - else if (defendedSquares & blockSq) - k += 4; + // Assign a larger bonus if the block square is defended. + if (defendedSquares & blockSq) + k += 5; bonus += make_score(k * w, k * w); } From 09caea5cab0b6c23fea8347a740bcc00083da6ab Mon Sep 17 00:00:00 2001 From: protonspring Date: Mon, 3 Jun 2019 07:16:33 -0600 Subject: [PATCH 28/54] Simplify Outposts #2176 This is a functional simplification. This is NOT the exact version that was tested. Beyond the testing, an assignment was removed and a piece changes for consistency. Instead of rewarding ANY square past an opponent pawn as an "outpost," only use squares that are protected by our pawn. I believe this is more consistent with what the chess world calls an "outpost." STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 23540 W: 5387 L: 5269 D: 12884 http://tests.stockfishchess.org/tests/view/5cf51e6d0ebc5925cf08b823 LTC LLR: 2.94 (-2.94,2.94) [-3.00,1.00] Total: 53085 W: 9271 L: 9204 D: 34610 http://tests.stockfishchess.org/tests/view/5cf5279e0ebc5925cf08b992 bench 3424592 --- src/evaluate.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 8c1b0b08..30c22a8e 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -141,7 +141,7 @@ namespace { constexpr Score KnightOnQueen = S( 16, 12); constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score MinorBehindPawn = S( 18, 3); - constexpr Score Outpost = S( 9, 3); + constexpr Score Outpost = S( 36, 12); constexpr Score PawnlessFlank = S( 17, 95); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RookOnPawn = S( 10, 32); @@ -305,14 +305,12 @@ namespace { if (Pt == BISHOP || Pt == KNIGHT) { // Bonus if piece is on an outpost square or can reach one - bb = OutpostRanks & ~pe->pawn_attacks_span(Them); + bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them); if (bb & s) - score += Outpost * (Pt == KNIGHT ? 4 : 2) - * ((attackedBy[Us][PAWN] & s) ? 2 : 1); + score += Outpost * (Pt == KNIGHT ? 2 : 1); - else if (bb &= b & ~pos.pieces(Us)) - score += Outpost * (Pt == KNIGHT ? 2 : 1) - * ((attackedBy[Us][PAWN] & bb) ? 2 : 1); + else if (bb & b & ~pos.pieces(Us)) + score += Outpost / (Pt == KNIGHT ? 1 : 2); // Knight and Bishop bonus for being right behind a pawn if (shift(pos.pieces(PAWN)) & s) From 2d06d659c0321ddfcf0781327e5175df0b0ff616 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 9 Jun 2019 15:26:53 +0300 Subject: [PATCH 29/54] Advanced pawn pushes tweak (#2175) passed STC http://tests.stockfishchess.org/tests/view/5cf586ee0ebc5925cf08c0ed LLR: 2.96 (-2.94,2.94) [0.50,4.50] Total: 29496 W: 6718 L: 6406 D: 16372 passed LTC http://tests.stockfishchess.org/tests/view/5cf59b630ebc5925cf08c343 LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 40778 W: 7057 L: 6765 D: 26956 original idea from early 2018 by @jerrydonaldwatson Code slightly rewritten to be shorter and more logical, no functinal changes compared to passed patch. --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index a3ce4c2d..11586787 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -958,7 +958,7 @@ moves_loop: // When in check, search starts from here if ( !captureOrPromotion && !givesCheck - && !pos.advanced_pawn_push(move)) + && (!pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg)) { // Move count based pruning (~30 Elo) if (moveCountPruning) From 6ed81f09ffa513f0938c1a16fa4edd55e552c178 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Sun, 9 Jun 2019 08:27:50 -0400 Subject: [PATCH 30/54] SEE Pruning Tweak (#2183) Don't SEE prune any check extensions STC (yellow): LLR: -2.96 (-2.94,2.94) [0.50,4.50] Total: 129934 W: 29390 L: 28905 D: 71639 http://tests.stockfishchess.org/tests/view/5cf6b1a70ebc5925cf08dedb LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 102115 W: 17692 L: 17224 D: 67199 http://tests.stockfishchess.org/tests/view/5cf830710ebc5925cf090331 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 11586787..f26ab959 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -984,7 +984,8 @@ moves_loop: // When in check, search starts from here if (!pos.see_ge(move, Value(-29 * lmrDepth * lmrDepth))) continue; } - else if (!pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) // (~20 Elo) + else if ((!givesCheck || !(pos.blockers_for_king(~us) & from_sq(move))) + && !pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) // (~20 Elo) continue; } From 5935daf8a5b6925a6af2084f87e3831b3bb17dac Mon Sep 17 00:00:00 2001 From: protonspring Date: Sun, 9 Jun 2019 06:28:42 -0600 Subject: [PATCH 31/54] Simplify WeakUnopposedPawn #2181 This is a functional simplification. Moves WeakUnopposedPawn to pawns.cpp and remove piece dependency. STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 8699 W: 2000 L: 1853 D: 4846 http://tests.stockfishchess.org/tests/view/5cf7721b0ebc5925cf08ee79 LTC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 46605 W: 7969 L: 7890 D: 30746 http://tests.stockfishchess.org/tests/view/5cf7d5f70ebc5925cf08fa96 --- src/evaluate.cpp | 5 ----- src/pawns.cpp | 7 ++++--- src/pawns.h | 2 -- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 30c22a8e..0da6ba4d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -152,7 +152,6 @@ namespace { constexpr Score ThreatBySafePawn = S(173, 94); constexpr Score TrappedRook = S( 47, 4); constexpr Score WeakQueen = S( 49, 15); - constexpr Score WeakUnopposedPawn = S( 12, 23); #undef S @@ -555,10 +554,6 @@ namespace { score += RestrictedPiece * popcount(b); - // Bonus for enemy unopposed weak pawns - if (pos.pieces(Us, ROOK, QUEEN)) - score += WeakUnopposedPawn * pe->weak_unopposed(Them); - // Find squares where our pawns can push on the next move b = shift(pos.pieces(Us, PAWN)) & ~pos.pieces(); b |= shift(b & TRank3BB) & ~pos.pieces(); diff --git a/src/pawns.cpp b/src/pawns.cpp index 291d40b6..47c89ed4 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -35,6 +35,7 @@ namespace { constexpr Score Backward = S( 9, 24); constexpr Score Doubled = S(11, 56); constexpr Score Isolated = S( 5, 15); + constexpr Score WeakUnopposed = S( 13, 27); // Connected pawn bonus constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; @@ -77,7 +78,7 @@ namespace { Bitboard ourPawns = pos.pieces( Us, PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN); - e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0; + e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0; e->kingSquares[Us] = SQ_NONE; e->pawnAttacks[Us] = pawn_attacks_bb(ourPawns); @@ -132,10 +133,10 @@ namespace { score += make_score(v, v * (r - 2) / 4); } else if (!neighbours) - score -= Isolated, e->weakUnopposed[Us] += !opposed; + score -= Isolated + WeakUnopposed * int(!opposed); else if (backward) - score -= Backward, e->weakUnopposed[Us] += !opposed; + score -= Backward + WeakUnopposed * int(!opposed); if (doubled && !support) score -= Doubled; diff --git a/src/pawns.h b/src/pawns.h index 771e9cd3..ef4b83f8 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -37,7 +37,6 @@ struct Entry { Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } - int weak_unopposed(Color c) const { return weakUnopposed[c]; } int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); } template @@ -59,7 +58,6 @@ struct Entry { Bitboard pawnAttacksSpan[COLOR_NB]; Square kingSquares[COLOR_NB]; Score kingSafety[COLOR_NB]; - int weakUnopposed[COLOR_NB]; int castlingRights[COLOR_NB]; int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares] }; From 14e23d520f251f28234a686a9d30d9711495dfef Mon Sep 17 00:00:00 2001 From: protonspring Date: Sun, 9 Jun 2019 06:31:16 -0600 Subject: [PATCH 32/54] Remove a few file_of's (simplify adjacent_files_bb) #2171 This is a non-functional simplification that removes two file_of(s). STC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 22030 W: 5106 L: 4984 D: 11940 http://tests.stockfishchess.org/tests/view/5cf028de0ebc5925cf0839e7 --- src/bitboard.h | 8 ++++---- src/pawns.cpp | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index 4e0267f1..ef5c4fa3 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -184,8 +184,8 @@ constexpr Bitboard pawn_double_attacks_bb(Bitboard b) { /// adjacent_files_bb() returns a bitboard representing all the squares on the /// adjacent files of the given one. -inline Bitboard adjacent_files_bb(File f) { - return shift(file_bb(f)) | shift(file_bb(f)); +inline Bitboard adjacent_files_bb(Square s) { + return shift(file_bb(s)) | shift(file_bb(s)); } @@ -221,7 +221,7 @@ inline Bitboard forward_file_bb(Color c, Square s) { /// starting from the given square. inline Bitboard pawn_attack_span(Color c, Square s) { - return forward_ranks_bb(c, s) & adjacent_files_bb(file_of(s)); + return forward_ranks_bb(c, s) & adjacent_files_bb(s); } @@ -229,7 +229,7 @@ inline Bitboard pawn_attack_span(Color c, Square s) { /// the given color and on the given square is a passed pawn. inline Bitboard passed_pawn_span(Color c, Square s) { - return forward_ranks_bb(c, s) & (adjacent_files_bb(file_of(s)) | file_bb(s)); + return forward_ranks_bb(c, s) & (adjacent_files_bb(s) | file_bb(s)); } diff --git a/src/pawns.cpp b/src/pawns.cpp index 47c89ed4..2b4f039e 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -87,7 +87,6 @@ namespace { { assert(pos.piece_on(s) == make_piece(Us, PAWN)); - File f = file_of(s); Rank r = relative_rank(Us, s); e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s); @@ -98,7 +97,7 @@ namespace { lever = theirPawns & PawnAttacks[Us][s]; leverPush = theirPawns & PawnAttacks[Us][s + Up]; doubled = ourPawns & (s - Up); - neighbours = ourPawns & adjacent_files_bb(f); + neighbours = ourPawns & adjacent_files_bb(s); phalanx = neighbours & rank_bb(s); support = neighbours & rank_bb(s - Up); From 53d197b841731840891c783bb7e044f012db4aa7 Mon Sep 17 00:00:00 2001 From: protonspring Date: Sun, 9 Jun 2019 06:33:34 -0600 Subject: [PATCH 33/54] Simplify passed pawns. (#2159) This is a functional simplification. If all of the stoppers are levers, a simple pawn push passes. STC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 41768 W: 9360 L: 9278 D: 23130 http://tests.stockfishchess.org/tests/view/5ce82ed60ebc5925cf073a79 LTC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 40463 W: 6964 L: 6875 D: 26624 http://tests.stockfishchess.org/tests/view/5ce87d0b0ebc5925cf07472b --- src/pawns.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 2b4f039e..d7848fbd 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -110,9 +110,8 @@ namespace { // full attack info to evaluate them. Include also not passed pawns // which could become passed after one or two pawn pushes when are // not attacked more times than defended. - if ( !(stoppers ^ lever ^ leverPush) - && (support || !more_than_one(lever)) - && popcount(phalanx) >= popcount(leverPush)) + if ( !(stoppers ^ lever) || + (!(stoppers ^ leverPush) && popcount(phalanx) >= popcount(leverPush))) e->passedPawns[Us] |= s; else if (stoppers == square_bb(s + Up) && r >= RANK_5) From 2ead74d1e2b0c5edbb0da5887e56bbddb5b2a905 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 9 Jun 2019 14:34:51 +0200 Subject: [PATCH 34/54] Remove depth condition for ttPv (#2166) Currently PV nodes with a depth <= 4 were ignored for ttPv. Now remove this constraint and use all PV nodes. STC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 52209 W: 11755 L: 11694 D: 28760 http://tests.stockfishchess.org/tests/view/5cebc2d30ebc5925cf07b93a LTC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 20874 W: 3689 L: 3568 D: 13617 http://tests.stockfishchess.org/tests/view/5cec01fc0ebc5925cf07c62d --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f26ab959..df777b3b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -603,7 +603,7 @@ namespace { ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ttHit ? tte->move() : MOVE_NONE; - ttPv = (ttHit && tte->is_pv()) || (PvNode && depth > 4 * ONE_PLY); + ttPv = PvNode || (ttHit && tte->is_pv()); // At non-PV nodes we check for an early TT cutoff if ( !PvNode From d39bc2efa197ba2fd55b68eced1c60bcfe2facc1 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Thu, 2 May 2019 19:36:25 +0200 Subject: [PATCH 35/54] Assorted trivial cleanups 5/2019 No functional change. bench: 4178282 --- Readme.md | 28 ++++++++++++++-------------- src/endgame.cpp | 35 ++++++++++++++++++----------------- src/endgame.h | 8 ++++---- src/evaluate.cpp | 8 +++----- src/main.cpp | 2 +- src/pawns.h | 1 - src/position.cpp | 12 ++++++------ src/search.cpp | 45 ++++++++++++++++++--------------------------- src/search.h | 2 +- src/tt.cpp | 7 ++++--- src/tt.h | 2 +- src/types.h | 5 +++-- 12 files changed, 73 insertions(+), 82 deletions(-) diff --git a/Readme.md b/Readme.md index bc058dbc..be324763 100644 --- a/Readme.md +++ b/Readme.md @@ -1,7 +1,7 @@ ## Overview [![Build Status](https://travis-ci.org/official-stockfish/Stockfish.svg?branch=master)](https://travis-ci.org/official-stockfish/Stockfish) -[![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish) +[![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?branch=master&svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master) [Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine derived from Glaurung 2.1. It is not a complete chess program and requires a @@ -34,11 +34,11 @@ Currently, Stockfish has the following UCI options: A positive value for contempt favors middle game positions and avoids draws. * #### Analysis Contempt - By default, contempt is set to prefer the side to move. Set this option to "White" + By default, contempt is set to prefer the side to move. Set this option to "White" or "Black" to analyse with contempt for that side, or "Off" to disable contempt. * #### Threads - The number of CPU threads used for searching a position. For best performance, set + The number of CPU threads used for searching a position. For best performance, set this equal to the number of CPU cores available. * #### Hash @@ -58,18 +58,18 @@ Currently, Stockfish has the following UCI options: Lower the Skill Level in order to make Stockfish play weaker. * #### Move Overhead - Assume a time delay of x ms due to network and GUI overheads. This is useful to + Assume a time delay of x ms due to network and GUI overheads. This is useful to avoid losses on time in those cases. * #### Minimum Thinking Time - Search for at least x ms per move. + Search for at least x ms per move. * #### Slow Mover - Lower values will make Stockfish take less time in games, higher values will + Lower values will make Stockfish take less time in games, higher values will make it think longer. * #### nodestime - Tells the engine to use nodes searched instead of wall time to account for + Tells the engine to use nodes searched instead of wall time to account for elapsed time. Useful for engine testing. * #### UCI_Chess960 @@ -79,13 +79,13 @@ Currently, Stockfish has the following UCI options: An option handled by your GUI. * #### SyzygyPath - Path to the folders/directories storing the Syzygy tablebase files. Multiple - directories are to be separated by ";" on Windows and by ":" on Unix-based + Path to the folders/directories storing the Syzygy tablebase files. Multiple + directories are to be separated by ";" on Windows and by ":" on Unix-based operating systems. Do not use spaces around the ";" or ":". - + Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6` - - It is recommended to store .rtbw files on an SSD. There is no loss in storing + + It is recommended to store .rtbw files on an SSD. There is no loss in storing the .rtbz files on a regular HD. It is recommended to verify all md5 checksums of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will lead to engine crashes. @@ -153,7 +153,7 @@ community effort. There are a few ways to help contribute to its growth. ### Donating hardware Improving Stockfish requires a massive amount of testing. You can donate -your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker) +your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker) and view the current tests on [Fishtest](http://tests.stockfishchess.org/tests). ### Improving the code @@ -169,7 +169,7 @@ generic rather than being focused on Stockfish's precise implementation. Nevertheless, a helpful resource. * The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish). -Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking) +Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking) group and engine testing is done on [Fishtest](http://tests.stockfishchess.org/tests). If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test) first, where the basics of Stockfish development are explained. diff --git a/src/endgame.cpp b/src/endgame.cpp index 7c4efa3c..4f8c2279 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -88,27 +88,28 @@ namespace Endgames { void init() { - add("KPK"); - add("KNNK"); - add("KBNK"); - add("KRKP"); - add("KRKB"); - add("KRKN"); - add("KQKP"); - add("KQKR"); - add("KNNKP"); + add("KPK"); + add("KNNK"); + add("KBNK"); + add("KRKP"); + add("KRKB"); + add("KRKN"); + add("KQKP"); + add("KQKR"); + add("KNNKP"); - add("KNPK"); - add("KNPKB"); - add("KRPKR"); - add("KRPKB"); - add("KBPKB"); - add("KBPKN"); - add("KBPPKB"); - add("KRPPKRP"); + add("KNPK"); + add("KNPKB"); + add("KRPKR"); + add("KRPKB"); + add("KBPKB"); + add("KBPKN"); + add("KBPPKB"); + add("KRPPKRP"); } } + /// Mate with KX vs K. This function is used to evaluate positions with /// king and plenty of material vs a lone king. It simply gives the /// attacking side a bonus for driving the defending king towards the edge diff --git a/src/endgame.h b/src/endgame.h index 81afb2e5..d0a5a97e 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -91,7 +91,7 @@ struct Endgame : public EndgameBase { }; -/// The Endgames class stores the pointers to endgame evaluation and scaling +/// The Endgames namespace handles the pointers to endgame evaluation and scaling /// base objects in two std::map. We use polymorphism to invoke the actual /// endgame function by calling its virtual operator(). @@ -99,9 +99,11 @@ namespace Endgames { template using Ptr = std::unique_ptr>; template using Map = std::map>; - + extern std::pair, Map> maps; + void init(); + template Map& map() { return std::get::value>(maps); @@ -119,8 +121,6 @@ namespace Endgames { const EndgameBase* probe(Key key) { return map().count(key) ? map()[key].get() : nullptr; } - - void init(); } #endif // #ifndef ENDGAME_H_INCLUDED diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 0da6ba4d..3ff1460b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -190,10 +190,8 @@ namespace { // color, including x-rays. But diagonal x-rays through pawns are not computed. Bitboard attackedBy2[COLOR_NB]; - // kingRing[color] are the squares adjacent to the king, plus (only for a - // king on its first rank) the squares two ranks in front. For instance, - // if black's king is on g8, kingRing[BLACK] is f8, h8, f7, g7, h7, f6, g6 - // and h6. + // kingRing[color] are the squares adjacent to the king plus some other + // very near squares, depending on king position. Bitboard kingRing[COLOR_NB]; // kingAttackersCount[color] is the number of pieces of the given color @@ -802,7 +800,7 @@ namespace { // Early exit if score is high Value v = (mg_value(score) + eg_value(score)) / 2; - if (abs(v) > (LazyThreshold + pos.non_pawn_material() / 64)) + if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64) return pos.side_to_move() == WHITE ? v : -v; // Main evaluation begins here diff --git a/src/main.cpp b/src/main.cpp index 57656f62..f94a322c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,8 +42,8 @@ int main(int argc, char* argv[]) { Bitboards::init(); Position::init(); Bitbases::init(); - Search::init(); Endgames::init(); + Search::init(); Threads.set(Options["Threads"]); Search::clear(); // After threads are up diff --git a/src/pawns.h b/src/pawns.h index ef4b83f8..a34e5e69 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -59,7 +59,6 @@ struct Entry { Square kingSquares[COLOR_NB]; Score kingSafety[COLOR_NB]; int castlingRights[COLOR_NB]; - int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares] }; typedef HashTable Table; diff --git a/src/position.cpp b/src/position.cpp index edb40499..14121d47 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -387,7 +387,7 @@ void Position::set_state(StateInfo* si) const { if (type_of(pc) == PAWN) si->pawnKey ^= Zobrist::psq[pc][s]; - else if (type_of(pc) != PAWN && type_of(pc) != KING) + else if (type_of(pc) != KING) si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc]; } @@ -491,7 +491,7 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners // Snipers are sliders that attack 's' when a piece and other snipers are removed Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK)) | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; - Bitboard occupancy = pieces() & ~snipers; + Bitboard occupancy = pieces() ^ snipers; while (snipers) { @@ -1192,10 +1192,10 @@ bool Position::has_game_cycle(int ply) const { if (ply > i) return true; - // For nodes before or at the root, check that the move is a repetition one - // rather than a move to the current position. - // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in the same - // location, so we have to select which square to check. + // For nodes before or at the root, check that the move is a + // repetition rather than a move to the current position. + // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in + // the same location, so we have to select which square to check. if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move()) continue; diff --git a/src/search.cpp b/src/search.cpp index df777b3b..0e10f44f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -149,7 +149,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int(22.9 * std::log(i)); + Reductions[i] = int(22.9 * std::log(i)); } @@ -240,17 +240,13 @@ void MainThread::search() { minScore = std::min(minScore, th->rootMoves[0].score); // Vote according to score and depth, and select the best thread - int64_t bestVote = 0; for (Thread* th : Threads) { votes[th->rootMoves[0].pv[0]] += - (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); + (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); - if (votes[th->rootMoves[0].pv[0]] > bestVote) - { - bestVote = votes[th->rootMoves[0].pv[0]]; + if (votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]) bestThread = th; - } } } @@ -538,13 +534,13 @@ namespace { bool ttHit, ttPv, inCheck, givesCheck, improving; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; Piece movedPiece; - int moveCount, captureCount, quietCount; + int moveCount, captureCount, quietCount, singularLMR; // Step 1. Initialize node Thread* thisThread = pos.this_thread(); inCheck = pos.checkers(); Color us = pos.side_to_move(); - moveCount = captureCount = quietCount = ss->moveCount = 0; + moveCount = captureCount = quietCount = singularLMR = ss->moveCount = 0; bestValue = -VALUE_INFINITE; maxValue = VALUE_INFINITE; @@ -589,10 +585,10 @@ namespace { // starts with statScore = 0. Later grandchildren start with the last calculated // statScore of the previous grandchild. This influences the reduction rules in // LMR which are based on the statScore of parent position. - if (rootNode) - (ss + 4)->statScore = 0; - else - (ss + 2)->statScore = 0; + if (rootNode) + (ss + 4)->statScore = 0; + else + (ss + 2)->statScore = 0; // Step 4. Transposition table lookup. We don't want the score of a partial // search to overwrite a previous full search TT value, so we use a different @@ -850,7 +846,6 @@ moves_loop: // When in check, search starts from here value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc moveCountPruning = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); - int singularExtensionLMRmultiplier = 0; // Step 12. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. @@ -907,12 +902,13 @@ moves_loop: // When in check, search starts from here ss->excludedMove = MOVE_NONE; if (value < singularBeta) - { + { extension = ONE_PLY; - singularExtensionLMRmultiplier++; + singularLMR++; + if (value < singularBeta - std::min(3 * depth / ONE_PLY, 39)) - singularExtensionLMRmultiplier++; - } + singularLMR++; + } // Multi-cut pruning // Our ttMove is assumed to fail high, and now we failed high also on a reduced @@ -1023,8 +1019,9 @@ moves_loop: // When in check, search starts from here // Decrease reduction if opponent's move count is high (~10 Elo) if ((ss-1)->moveCount > 15) r -= ONE_PLY; + // Decrease reduction if move has been singularly extended - r -= singularExtensionLMRmultiplier * ONE_PLY; + r -= singularLMR * ONE_PLY; if (!captureOrPromotion) { @@ -1060,7 +1057,7 @@ moves_loop: // When in check, search starts from here r -= ss->statScore / 20000 * ONE_PLY; } - Depth d = std::max(newDepth - std::max(r, DEPTH_ZERO), ONE_PLY); + Depth d = clamp(newDepth - r, ONE_PLY, newDepth); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); @@ -1476,7 +1473,7 @@ moves_loop: // When in check, search starts from here void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCount, int bonus) { - CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory; + CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory; Piece moved_piece = pos.moved_piece(move); PieceType captured = type_of(pos.piece_on(to_sq(move))); @@ -1715,10 +1712,4 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { if (dtz_available || rootMoves[0].tbScore <= VALUE_DRAW) Cardinality = 0; } - else - { - // Assign the same rank to all moves - for (auto& m : rootMoves) - m.tbRank = 0; - } } diff --git a/src/search.h b/src/search.h index 92e124fc..24c58d30 100644 --- a/src/search.h +++ b/src/search.h @@ -69,7 +69,7 @@ struct RootMove { Value score = -VALUE_INFINITE; Value previousScore = -VALUE_INFINITE; int selDepth = 0; - int tbRank; + int tbRank = 0; Value tbScore; std::vector pv; }; diff --git a/src/tt.cpp b/src/tt.cpp index b8fe7567..6121b3ad 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -43,15 +43,16 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) // Overwrite less valuable entries if ( (k >> 48) != key16 - || d / ONE_PLY + 10 > depth8 + ||(d - DEPTH_OFFSET) / ONE_PLY > depth8 - 4 || b == BOUND_EXACT) { + assert((d - DEPTH_OFFSET) / ONE_PLY >= 0); + key16 = (uint16_t)(k >> 48); value16 = (int16_t)v; eval16 = (int16_t)ev; genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); - assert((d - DEPTH_NONE) / ONE_PLY >= 0); - depth8 = (uint8_t)((d - DEPTH_NONE) / ONE_PLY); + depth8 = (uint8_t)((d - DEPTH_OFFSET) / ONE_PLY); } } diff --git a/src/tt.h b/src/tt.h index 3608b77c..3a5ba5da 100644 --- a/src/tt.h +++ b/src/tt.h @@ -40,7 +40,7 @@ struct TTEntry { Move move() const { return (Move )move16; } Value value() const { return (Value)value16; } Value eval() const { return (Value)eval16; } - Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)) + DEPTH_NONE; } + Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)) + DEPTH_OFFSET; } bool is_pv() const { return (bool)(genBound8 & 0x4); } Bound bound() const { return (Bound)(genBound8 & 0x3); } void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev); diff --git a/src/types.h b/src/types.h index b0758f43..bee6538f 100644 --- a/src/types.h +++ b/src/types.h @@ -213,8 +213,9 @@ enum Depth : int { DEPTH_QS_NO_CHECKS = -1 * ONE_PLY, DEPTH_QS_RECAPTURES = -5 * ONE_PLY, - DEPTH_NONE = -6 * ONE_PLY, - DEPTH_MAX = MAX_PLY * ONE_PLY + DEPTH_NONE = -6 * ONE_PLY, + DEPTH_OFFSET = DEPTH_NONE, + DEPTH_MAX = MAX_PLY * ONE_PLY }; static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2"); From a9cca5c953e6ccec865102b13b47b9f45d98a0fc Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Sun, 9 Jun 2019 18:26:47 -0400 Subject: [PATCH 36/54] No DC prune in QS (#2185) Don't prune discover checks in qSearch STC: LLR: 2.96 (-2.94,2.94) [0.50,4.50] Total: 23176 W: 5320 L: 5039 D: 12817 http://tests.stockfishchess.org/tests/view/5cfbc9350ebc5925cf094ab3 LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 128428 W: 22222 L: 21679 D: 84527 http://tests.stockfishchess.org/tests/view/5cfbf0b70ebc5925cf094ebc Bench: 3883245 --- src/search.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/search.cpp b/src/search.cpp index 0e10f44f..50bce13c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1363,6 +1363,7 @@ moves_loop: // When in check, search starts from here // Don't search moves with negative SEE values if ( (!inCheck || evasionPrunable) + && (!givesCheck || !(pos.blockers_for_king(~pos.side_to_move()) & from_sq(move))) && !pos.see_ge(move)) continue; From f9518de974fdf1931dbead736364efce9537320b Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Fri, 14 Jun 2019 07:36:42 +0200 Subject: [PATCH 37/54] Increase pawns cache (#2187) Increase size of the pawns table by the factor 8. This decreases the number of recalculations of pawn structure information significantly (at least at LTC). I have done measurements for different depths and pawn cache sizes. First are given the number of pawn entry calculations are done (in parentheses is the frequency that a call to probe triggers a pawn entry calculation). The delta% are the percentage of less done pawn entry calculations in comparison to master VSTC: bench 1 1 12 STC: bench 8 1 16 LTC: bench 64 1 20 VLTC: bench 512 1 24 VSTC STC LTC VLTC master 82218(6%) 548935(6%) 2415422(7%) 9548071(7%) pawncache*2 79859(6%) 492943(5%) 2084794(6%) 8275206(6%) pawncache*4 78551(6%) 458758(5%) 1827770(5%) 7112531(5%) pawncache*8 77963(6%) 439421(4%) 1649169(5%) 6128652(4%) delta%(p2-m) -2.9% -10.2% -13.7% -13.3% delta%(p4-m) -4.5% -16.4% -24.3% -25.5% delta%(p8-m) -5.2% -20.0% -31.7% -35.8% STC: (non-regression test because at STC the effect is smaller than at LTC) LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 22767 W: 5160 L: 5040 D: 12567 http://tests.stockfishchess.org/tests/view/5d00f6040ebc5925cf09c3e2 LTC: LLR: 2.94 (-2.94,2.94) [0.00,4.00] Total: 26340 W: 4524 L: 4286 D: 17530 http://tests.stockfishchess.org/tests/view/5d00a3810ebc5925cf09ba16 No functional change. --- src/pawns.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pawns.h b/src/pawns.h index a34e5e69..1f930988 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -61,7 +61,7 @@ struct Entry { int castlingRights[COLOR_NB]; }; -typedef HashTable Table; +typedef HashTable Table; Entry* probe(const Position& pos); From 8cfe27b76521f3c357b8d3542717aceaa5eee23b Mon Sep 17 00:00:00 2001 From: protonspring Date: Fri, 14 Jun 2019 00:22:02 -0600 Subject: [PATCH 38/54] Remove backmost_sq (#2190) This is a non-functional simplification. backmost_sq and frontmost_sq are redundant. It seems quite clear to always use frontmost_sq and use the correct color. Non functional change. --- src/bitboard.h | 5 +---- src/endgame.cpp | 2 +- src/pawns.cpp | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index ef5c4fa3..7a16597d 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -376,10 +376,7 @@ inline Square pop_lsb(Bitboard* b) { } -/// frontmost_sq() and backmost_sq() return the most/least advanced square in -/// the given bitboard relative to the given color. - +/// frontmost_sq() returns the most advanced square for the given color inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); } -inline Square backmost_sq(Color c, Bitboard b) { return c == WHITE ? lsb(b) : msb(b); } #endif // #ifndef BITBOARD_H_INCLUDED diff --git a/src/endgame.cpp b/src/endgame.cpp index 4f8c2279..e10f8d5d 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -365,7 +365,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && pos.count(weakSide) >= 1) { // Get weakSide pawn that is closest to the home rank - Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN)); + Square weakPawnSq = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN)); Square strongKingSq = pos.square(strongSide); Square weakKingSq = pos.square(weakSide); diff --git a/src/pawns.cpp b/src/pawns.cpp index d7848fbd..bbcadceb 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -190,7 +190,7 @@ void Entry::evaluate_shelter(const Position& pos, Square ksq, Score& shelter) { for (File f = File(center - 1); f <= File(center + 1); ++f) { b = ourPawns & file_bb(f); - Rank ourRank = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1; + Rank ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; b = theirPawns & file_bb(f); Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; From 46ce245763705c89dba60dcfda549dc1f64eb48b Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Fri, 14 Jun 2019 13:59:17 -0400 Subject: [PATCH 39/54] Simplify SEE Pruning (#2191) Simplify SEE Pruning Note this should also be a speedup... If givesCheck is extended we know (except for DC) that it will have a positive SEE. So this new logic will be triggered before doing another expensive SEE function. STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 24429 W: 5484 L: 5368 D: 13577 http://tests.stockfishchess.org/tests/view/5cffbccd0ebc5925cf09a154 LTC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 28428 W: 4873 L: 4765 D: 18790 http://tests.stockfishchess.org/tests/view/5d0015f60ebc5925cf09acb1 Bench: 3897263 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 50bce13c..d9543899 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -980,7 +980,7 @@ moves_loop: // When in check, search starts from here if (!pos.see_ge(move, Value(-29 * lmrDepth * lmrDepth))) continue; } - else if ((!givesCheck || !(pos.blockers_for_king(~us) & from_sq(move))) + else if ((!givesCheck || !extension) && !pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) // (~20 Elo) continue; } From 466daf6fbafd1f58f064c2fa03d2fa2c1315c4f5 Mon Sep 17 00:00:00 2001 From: syzygy1 <3028851+syzygy1@users.noreply.github.com> Date: Tue, 18 Jun 2019 23:27:34 +0200 Subject: [PATCH 40/54] Partial revert of "Assorted trivial cleanups 5/2019". Since root_probe() and root_probe_wdl() do not reset all tbRank values if they fail, it is necessary to do this in rank_root_move(). This fixes issue #2196. Alternatively, the loop could be moved into both root_probe() and root_probe_wdl(). No functional change --- src/search.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index d9543899..047a0892 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1713,4 +1713,10 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { if (dtz_available || rootMoves[0].tbScore <= VALUE_DRAW) Cardinality = 0; } + else + { + // Clean up if root_probe() and root_probe_wdl() have failed + for (auto& m : rootMoves) + m.tbRank = 0; + } } From 59f1d0c7dd3fcca307a18ff493ce07e290754a2b Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 13 Jun 2019 22:32:23 +0200 Subject: [PATCH 41/54] Fix progress issue with shuffling extensions Fixes issues #2126 and #2189 where no progress in rootDepth is made for particular fens: 8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1 8/1r1rp1k1/1b1pPp2/2pP1Pp1/1pP3Pp/pP5P/P5K1/8 w - - 79 46 the cause are the shuffle extensions. Upon closer analysis, it appears that in these cases a shuffle extension is made for every node searched, and progess can not be made. This patch implements a fix, namely to limit the number of extensions relative to the number of nodes searched. The ratio employed is 1/4, which fixes the issues seen so far, but it is a heuristic, and I expect that certain positions might require an even smaller fraction. The patch was tested as a bug fix and passed: STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 56601 W: 12633 L: 12581 D: 31387 http://tests.stockfishchess.org/tests/view/5d02b37a0ebc5925cf09f6da LTC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 52042 W: 8907 L: 8837 D: 34298 http://tests.stockfishchess.org/tests/view/5d0319420ebc5925cf09fe57 Furthermore, to confirm that the shuffle extension in this form indeed still brings Elo, one more test at VLTC was performed: VLTC: LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 142022 W: 20963 L: 20435 D: 100624 http://tests.stockfishchess.org/tests/view/5d03630d0ebc5925cf0a011a Bench: 3961247 --- src/search.cpp | 2 +- src/thread.cpp | 2 +- src/thread.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 047a0892..9919b0cc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -932,7 +932,7 @@ moves_loop: // When in check, search starts from here else if ( PvNode && pos.rule50_count() > 18 && depth < 3 * ONE_PLY - && ss->ply < 3 * thisThread->rootDepth / ONE_PLY) // To avoid too deep searches + && ++thisThread->shuffleExts < thisThread->nodes.load(std::memory_order_relaxed) / 4) // To avoid too many extensions extension = ONE_PLY; // Passed pawn extension diff --git a/src/thread.cpp b/src/thread.cpp index 2f1237a3..e5043b6e 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -191,7 +191,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, for (Thread* th : *this) { - th->nodes = th->tbHits = th->nmpMinPly = 0; + th->shuffleExts = th->nodes = th->tbHits = th->nmpMinPly = 0; th->rootDepth = th->completedDepth = DEPTH_ZERO; th->rootMoves = rootMoves; th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); diff --git a/src/thread.h b/src/thread.h index 114769d2..c11d1787 100644 --- a/src/thread.h +++ b/src/thread.h @@ -59,7 +59,7 @@ public: Pawns::Table pawnsTable; Material::Table materialTable; - size_t pvIdx, pvLast; + size_t pvIdx, pvLast, shuffleExts; int selDepth, nmpMinPly; Color nmpColor; std::atomic nodes, tbHits, bestMoveChanges; From 297c40291a1cc0ca27fecef342501ba1c359f9cd Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Mon, 17 Jun 2019 17:35:03 -0400 Subject: [PATCH 42/54] QuietPick Speed-up Non-functional speedup: no need to generate, score, or sort quiet moves if SkipQuiet is true. Thanks to @mstembera for his suggestion. STC: LLR: 2.95 (-2.94,2.94) [0.00,4.00] Total: 27910 W: 6406 L: 6129 D: 15375 http://tests.stockfishchess.org/tests/view/5d07e0920ebc5925cf0a58a8 Closes https://github.com/official-stockfish/Stockfish/pull/2194 No functional change --- src/movepick.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index a70785e7..52ac5b4c 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -200,11 +200,15 @@ top: /* fallthrough */ case QUIET_INIT: - cur = endBadCaptures; - endMoves = generate(pos, cur); + if (!skipQuiets) + { + cur = endBadCaptures; + endMoves = generate(pos, cur); - score(); - partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY); + score(); + partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY); + } + ++stage; /* fallthrough */ From 8bf21a723e77399382f05c75e45201882772684a Mon Sep 17 00:00:00 2001 From: Miguel Lahoz Date: Sat, 15 Jun 2019 14:01:02 +0800 Subject: [PATCH 43/54] Change multi-cut pruning condition Use comparison of eval with beta to predict potential cutNodes. This allows multi-cut pruning to also prune possibly mislabeled Pv and NonPv nodes. STC: LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 54305 W: 12302 L: 11867 D: 30136 http://tests.stockfishchess.org/tests/view/5d048ba50ebc5925cf0a15e8 LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 189512 W: 32620 L: 31904 D: 124988 http://tests.stockfishchess.org/tests/view/5d04bf740ebc5925cf0a17f0 Normally I would think such changes are risky, specially for PvNodes, but after trying a few other versions, it seems this version is more sound than I initially thought. Aside from this, a small funtional change is made to return singularBeta instead of beta to be more consistent with the fail-soft logic used in other parts of search. ============================= How to continue from there ? We could try to audit other parts of the search where the "cutNode" variable is used, and try to use dynamic info based on heuristic eval rather than on this variable, to check if the idea behind this patch could also be applied successfuly. Bench: 3503788 --- src/search.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9919b0cc..f682da3a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -913,10 +913,11 @@ moves_loop: // When in check, search starts from here // Multi-cut pruning // Our ttMove is assumed to fail high, and now we failed high also on a reduced // search without the ttMove. So we assume this expected Cut-node is not singular, - // that is multiple moves fail high, and we can prune the whole subtree by returning - // the hard beta bound. - else if (cutNode && singularBeta > beta) - return beta; + // that multiple moves fail high, and we can prune the whole subtree by returning + // a soft bound. + else if ( eval >= beta + && singularBeta >= beta) + return singularBeta; } // Check extension (~2 Elo) From 37ffacf2094594314346bf9c3d7d8a61911b34d5 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Thu, 20 Jun 2019 03:05:57 +0300 Subject: [PATCH 44/54] More bonus for free passed pawn Give even more bonus to passed pawn if adjacent squares to its path are not attacked. passed STC http://tests.stockfishchess.org/tests/view/5d08c9b10ebc5925cf0a6630 LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 175197 W: 39859 L: 38816 D: 96522 passed LTC http://tests.stockfishchess.org/tests/view/5d0ab8240ebc5925cf0a8fe4 LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 92928 W: 16124 L: 15682 D: 61122 Bench: 3398333 --- src/evaluate.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3ff1460b..cf478909 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -605,6 +605,7 @@ namespace { }; Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares; + Bitboard wideUnsafeSquares; Score score = SCORE_ZERO; b = pe->passed_pawns(Us); @@ -639,6 +640,7 @@ namespace { // consider all the squaresToQueen. Otherwise consider only the squares // in the pawn's path attacked or occupied by the enemy. defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s); + wideUnsafeSquares = AllSquares; bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN); @@ -647,10 +649,14 @@ namespace { if (!(pos.pieces(Them) & bb)) unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them); + + if (!unsafeSquares) + wideUnsafeSquares = (attackedBy[Them][ALL_PIECES] | pos.pieces(Them)) + & (shift(squaresToQueen) | shift(squaresToQueen)); // If there aren't any enemy attacks, assign a big bonus. Otherwise // assign a smaller bonus if the block square isn't attacked. - int k = !unsafeSquares ? 20 : !(unsafeSquares & blockSq) ? 9 : 0; + int k = !wideUnsafeSquares ? 35 : !unsafeSquares ? 20 : !(unsafeSquares & blockSq) ? 9 : 0; // Assign a larger bonus if the block square is defended. if (defendedSquares & blockSq) From 7cb8817ef2194737140410b07997fde9f777ef32 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Fri, 21 Jun 2019 10:04:31 +0200 Subject: [PATCH 45/54] Rewrite "More bonus for free passed pawn" -removes wideUnsafeSquares bitboard -removes a couple of bitboard operations -removes one if operator -updates comments so they actually represent what this part of code is doing now. passed non-regression STC http://tests.stockfishchess.org/tests/view/5d0c1ae50ebc5925cf0aa8db LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 16892 W: 3865 L: 3733 D: 9294 No functional change --- src/evaluate.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index cf478909..da76de10 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -605,7 +605,6 @@ namespace { }; Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares; - Bitboard wideUnsafeSquares; Score score = SCORE_ZERO; b = pe->passed_pawns(Us); @@ -636,11 +635,8 @@ namespace { // If the pawn is free to advance, then increase the bonus if (pos.empty(blockSq)) { - // If there is a rook or queen attacking/defending the pawn from behind, - // consider all the squaresToQueen. Otherwise consider only the squares - // in the pawn's path attacked or occupied by the enemy. - defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s); - wideUnsafeSquares = AllSquares; + defendedSquares = squaresToQueen = forward_file_bb(Us, s); + unsafeSquares = passed_pawn_span(Us, s); bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN); @@ -649,14 +645,14 @@ namespace { if (!(pos.pieces(Them) & bb)) unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them); - - if (!unsafeSquares) - wideUnsafeSquares = (attackedBy[Them][ALL_PIECES] | pos.pieces(Them)) - & (shift(squaresToQueen) | shift(squaresToQueen)); - // If there aren't any enemy attacks, assign a big bonus. Otherwise - // assign a smaller bonus if the block square isn't attacked. - int k = !wideUnsafeSquares ? 35 : !unsafeSquares ? 20 : !(unsafeSquares & blockSq) ? 9 : 0; + // If there are no enemy attacks on passed pawn span, assign a big bonus. + // Otherwise assign a smaller bonus if the path to queen is not attacked + // and even smaller bonus if it is attacked but block square is not. + int k = !unsafeSquares ? 35 : + !(unsafeSquares & squaresToQueen) ? 20 : + !(unsafeSquares & blockSq) ? 9 : + 0 ; // Assign a larger bonus if the block square is defended. if (defendedSquares & blockSq) From 4c986b050111e4fb96716a75ae2168c005b03df9 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 21 Jun 2019 16:24:28 +0200 Subject: [PATCH 46/54] Make the debug counters thread safe. needed to use them in a threaded run. No functional change. --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 8d3b202d..b1539ce2 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -145,7 +145,7 @@ const string engine_info(bool to_uci) { /// Debug functions used mainly to collect run-time statistics -static int64_t hits[2], means[2]; +static std::atomic hits[2], means[2]; void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; } void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); } From a8de07cc26999e2fef7298a63bfe349aaa4650fa Mon Sep 17 00:00:00 2001 From: joergoster Date: Thu, 27 Jun 2019 08:56:35 +0200 Subject: [PATCH 47/54] Improve multiPV mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skip all moves during the Non-PV (zero-window) search which will be searched as PV moves later anyways. We also wake sure the moves will be reported to the GUI despite they're not being searched — some GUIs may get confused otherwise, and it would unnecessarily complicate the code. Tested with MultiPV=4 STC http://tests.stockfishchess.org/tests/view/5ce7137c0ebc5925cf070d69 LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 8233 W: 3708 L: 3424 D: 1101 LTC http://tests.stockfishchess.org/tests/view/5ce798d60ebc5925cf071d17 LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 7369 W: 3197 L: 2911 D: 1261 Closes https://github.com/official-stockfish/Stockfish/pull/2163 No functional change. (in single PV mode) --- src/search.cpp | 8 +++++++- src/thread.h | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f682da3a..37fc2ec4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -292,7 +292,7 @@ void Thread::search() { bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; - size_t multiPV = Options["MultiPV"]; + multiPV = Options["MultiPV"]; Skill skill(Options["Skill Level"]); // When playing with strength handicap enable MultiPV search that we will @@ -870,6 +870,12 @@ moves_loop: // When in check, search starts from here sync_cout << "info depth " << depth / ONE_PLY << " currmove " << UCI::move(move, pos.is_chess960()) << " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl; + + // In MultiPV mode also skip moves which will be searched later as PV moves + if (rootNode && std::count(thisThread->rootMoves.begin() + thisThread->pvIdx + 1, + thisThread->rootMoves.begin() + thisThread->multiPV, move)) + continue; + if (PvNode) (ss+1)->pv = nullptr; diff --git a/src/thread.h b/src/thread.h index c11d1787..46ddb495 100644 --- a/src/thread.h +++ b/src/thread.h @@ -59,7 +59,7 @@ public: Pawns::Table pawnsTable; Material::Table materialTable; - size_t pvIdx, pvLast, shuffleExts; + size_t pvIdx, multiPV, pvLast, shuffleExts; int selDepth, nmpMinPly; Color nmpColor; std::atomic nodes, tbHits, bestMoveChanges; From 8b4521df83fb08c1b76e99b0ef9d4611b22bfbf3 Mon Sep 17 00:00:00 2001 From: Sergei Ivanov Date: Sat, 25 May 2019 15:30:32 +0300 Subject: [PATCH 48/54] Do not define increment operators on Value, Depth and Direction These operators are never used and do not make sense for these types. No functional change. --- src/types.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/types.h b/src/types.h index bee6538f..b9c01fe7 100644 --- a/src/types.h +++ b/src/types.h @@ -291,7 +291,6 @@ inline T& operator--(T& d) { return d = T(int(d) - 1); } #define ENABLE_FULL_OPERATORS_ON(T) \ ENABLE_BASE_OPERATORS_ON(T) \ -ENABLE_INCR_OPERATORS_ON(T) \ constexpr T operator*(int i, T d) { return T(i * int(d)); } \ constexpr T operator*(T d, int i) { return T(int(d) * i); } \ constexpr T operator/(T d, int i) { return T(int(d) / i); } \ From dab66631e8e31b86f91475f4245c4be6b36a37de Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Thu, 27 Jun 2019 09:21:50 +0200 Subject: [PATCH 49/54] Introduce attacks on space area This patch introduces a small malus for every square in our space mask that is attacked by enemy. The value of the malus is completely arbitrary and is something we can tweak, also maybe we can gain some elo with tweaking space threshold after this addition. Passed STC http://tests.stockfishchess.org/tests/view/5d10ce590ebc5925cf0af30b LLR: 2.96 (-2.94,2.94) [0.50,4.50] Total: 7082 W: 1648 L: 1449 D: 3985 Passed LTC http://tests.stockfishchess.org/tests/view/5d10d2d80ebc5925cf0af3fd LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 79494 W: 13727 L: 13324 D: 52443 Closes https://github.com/official-stockfish/Stockfish/pull/2207 bench 3516460 --- src/evaluate.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index da76de10..f2e822d0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -133,6 +133,7 @@ namespace { }; // Assorted bonuses and penalties + constexpr Score AttacksOnSpaceArea = S( 4, 0); constexpr Score BishopPawns = S( 3, 7); constexpr Score CorneredBishop = S( 50, 50); constexpr Score FlankAttacks = S( 8, 0); @@ -711,6 +712,8 @@ namespace { int weight = pos.count(Us) - 1; Score score = make_score(bonus * weight * weight / 16, 0); + score -= AttacksOnSpaceArea * popcount(attackedBy[Them][ALL_PIECES] & behind & safe); + if (T) Trace::add(SPACE, Us, score); From d889bb47185e33012b45cab63359952247bc86e2 Mon Sep 17 00:00:00 2001 From: protonspring Date: Thu, 27 Jun 2019 09:45:53 +0200 Subject: [PATCH 50/54] Bonus for double attacks on unsupported pawns This is a functional change that rewards double attacks on an unsupported pawns. STC (non-functional difference) LLR: 2.96 (-2.94,2.94) [0.50,4.50] Total: 83276 W: 18981 L: 18398 D: 45897 http://tests.stockfishchess.org/tests/view/5d0970500ebc5925cf0a77d4 LTC (incomplete looping version) LLR: 0.50 (-2.94,2.94) [0.00,3.50] Total: 82999 W: 14244 L: 13978 D: 54777 http://tests.stockfishchess.org/tests/view/5d0a8d480ebc5925cf0a8d58 LTC (completed non-looping version). LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 223381 W: 38323 L: 37512 D: 147546 http://tests.stockfishchess.org/tests/view/5d0e80510ebc5925cf0ad320 Closes https://github.com/official-stockfish/Stockfish/pull/2205 Bench 3633546 ---------------------------------- Comments by Alain SAVARD: interesting result ! I would have expected that search would resolve such positions correctly on the very next move. This is not a very common pattern, and when it happens, it will quickly disappear. So I'm quite surprised that it passed LTC. I would be even more surprised if this would resist a simplification. Anyway, let's try to imagine a few cases. a) If you have White d5 f5 against Black e6, and White to move last move by Black was probably a capture on e6 and White is about to recapture on e6 b) If you have White d5 f5 against e6, and Black to move last move by White was possibly a capture on d5 or f5 or the pawn on e6 was pinned or could not move for some reason. and white wants to blast open the position and just pushed d4-d5 or f4-f5 Some possible follow-ups a) Motif is so rare that the popcount() can be safely replaced with a bool() But this would not pass a SPRT[0,4], So try a simplification with bool() and also without the & ~theirAttacks b) If it works, we probably can simply have this in the loop if (lever) score += S(0, 20); c) remove all this and tweak something in search for pawn captures (priority, SEE, extension,..) --- src/movepick.cpp | 2 +- src/pawns.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 52ac5b4c..b7f66178 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -208,7 +208,7 @@ top: score(); partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY); } - + ++stage; /* fallthrough */ diff --git a/src/pawns.cpp b/src/pawns.cpp index bbcadceb..54748504 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -36,6 +36,7 @@ namespace { constexpr Score Doubled = S(11, 56); constexpr Score Isolated = S( 5, 15); constexpr Score WeakUnopposed = S( 13, 27); + constexpr Score Attacked2Unsupported = S( 0, 20); // Connected pawn bonus constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; @@ -79,8 +80,13 @@ namespace { Bitboard theirPawns = pos.pieces(Them, PAWN); e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0; - e->kingSquares[Us] = SQ_NONE; - e->pawnAttacks[Us] = pawn_attacks_bb(ourPawns); + e->kingSquares[Us] = SQ_NONE; + e->pawnAttacks[Us] = pawn_attacks_bb(ourPawns); + + // Unsupported enemy pawns attacked twice by us + score += Attacked2Unsupported * popcount( theirPawns + & pawn_double_attacks_bb(ourPawns) + & ~pawn_attacks_bb(theirPawns)); // Loop through all pawns of the current color and score each pawn while ((s = *pl++) != SQ_NONE) From c9d73d1aa5cf609b626776a112cd699339fefb67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Oster?= Date: Sun, 30 Jun 2019 15:16:20 +0200 Subject: [PATCH 51/54] Try to get a more precise bench time (#2211) Initialization of larger hash sizes can take some time. Don't include this time in the bench by resetting the timer after Search::clear(). Also move 'ucinewgame' command down in the list, so that it is processed after the configuration of Threads and Hash size. No functional change. --- src/benchmark.cpp | 2 +- src/uci.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 51bd7949..b23c5d17 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -139,9 +139,9 @@ vector setup_bench(const Position& current, istream& is) { file.close(); } - list.emplace_back("ucinewgame"); list.emplace_back("setoption name Threads value " + threads); list.emplace_back("setoption name Hash value " + ttSize); + list.emplace_back("ucinewgame"); for (const string& fen : fens) if (fen.find("setoption") != string::npos) diff --git a/src/uci.cpp b/src/uci.cpp index 739cf343..a4235f2b 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -164,7 +164,7 @@ namespace { } else if (token == "setoption") setoption(is); else if (token == "position") position(pos, is, states); - else if (token == "ucinewgame") Search::clear(); + else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take some while } elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero' From 79d06d8840110713ca135ca53f2347a57c6494e2 Mon Sep 17 00:00:00 2001 From: protonspring Date: Sun, 30 Jun 2019 07:22:37 -0600 Subject: [PATCH 52/54] Move storm special condition to UnblockedStorm array (#2210) This is a functional simplification. Looks like we can accommodate the special initialization of Value in evaluate_shelter in the UnblockedStorm array. STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 32483 W: 7422 L: 7322 D: 17739 http://tests.stockfishchess.org/tests/view/5d14c5f80ebc5925cf0b48da LTC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 35361 W: 6139 L: 6042 D: 23180 http://tests.stockfishchess.org/tests/view/5d14d69c0ebc5925cf0b4bd0 Bench 3596270 --- src/pawns.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 54748504..c85d4fd9 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -53,11 +53,12 @@ namespace { // Danger of enemy pawns moving toward our king by [distance from edge][rank]. // RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn // is behind our king. + // [0][1-2] accommodate opponent pawn on edge (likely blocked by our king) constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = { - { V( 89), V(107), V(123), V(93), V(57), V( 45), V( 51) }, - { V( 44), V(-18), V(123), V(46), V(39), V( -7), V( 23) }, - { V( 4), V( 52), V(162), V(37), V( 7), V(-14), V( -2) }, - { V(-10), V(-14), V( 90), V(15), V( 2), V( -7), V(-16) } + { V( 89), V(-285), V(-185), V(93), V(57), V( 45), V( 51) }, + { V( 44), V( -18), V( 123), V(46), V(39), V( -7), V( 23) }, + { V( 4), V( 52), V( 162), V(37), V( 7), V(-14), V( -2) }, + { V(-10), V( -14), V( 90), V(15), V( 2), V( -7), V(-16) } }; #undef S @@ -181,16 +182,12 @@ template void Entry::evaluate_shelter(const Position& pos, Square ksq, Score& shelter) { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); - constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH); - constexpr Bitboard BlockSquares = (Rank1BB | Rank2BB | Rank7BB | Rank8BB) - & (FileABB | FileHBB); Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq); Bitboard ourPawns = b & pos.pieces(Us); Bitboard theirPawns = b & pos.pieces(Them); - Value bonus[] = { (shift(theirPawns) & BlockSquares & ksq) ? Value(374) : Value(5), - VALUE_ZERO }; + Value bonus[] = { Value(5), Value(5) }; File center = clamp(file_of(ksq), FILE_B, FILE_G); for (File f = File(center - 1); f <= File(center + 1); ++f) From 217840a6a5a40b516cab59a450a9f36352997240 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 1 Jul 2019 14:07:23 +0200 Subject: [PATCH 53/54] Introduce coordination between searching threads (#2204) this patch improves threading performance by introducing some coordination between threads. The observation is that threading is an area where a lot of Elo can potentially be gained: https://github.com/glinscott/fishtest/wiki/UsefulData#elo-from-threading At STC, 8 threads gain roughly 320 Elo, vs sequential at the same time, however, loses 66 Elo against a single thread with 8x more time. This 66 Elo should be partially recoverable with improved threading. To improve threading, this patch introduces some LMR at nodes that are already being searched by other threads. This requires some coordination between threads, avoiding however synchronisation. To do so, threads leave a trail of breadcrumbs to mark the nodes they are searching. These breadcrumbs are stored in a small hash table, which is only probed at low plies (currently ply < 8). A couple of variants of this patch passed both STC and LTC threaded tests. I picked the simpler, more robust version. I expect that further tests can find further improvements. STC (5+0.05 @ 8 threads): LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 26209 W: 5359 L: 5079 D: 15771 http://tests.stockfishchess.org/tests/view/5d0a9b030ebc5925cf0a8e6f LTC (20+0.2 @ 8 threads): LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 34832 W: 5650 L: 5382 D: 23800 http://tests.stockfishchess.org/tests/view/5d0c67a20ebc5925cf0aafa7 other passed/tested variants: http://tests.stockfishchess.org/tests/view/5d0a9b030ebc5925cf0a8e6f http://tests.stockfishchess.org/tests/view/5d0c67ca0ebc5925cf0aafa9 http://tests.stockfishchess.org/tests/view/5d0c67810ebc5925cf0aafa3 http://tests.stockfishchess.org/tests/view/5d0958ca0ebc5925cf0a74c6 For the sequential code there is no change in bench, and an earlier version of this patch passed a non-regression test. STC (10+0.1 @ 1 thread) LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 10471 W: 2364 L: 2220 D: 5887 http://tests.stockfishchess.org/tests/view/5d087ee20ebc5925cf0a6381 passed the additional non-regression tests at 2 and 4 threads 20+0.2 TC. The code was rebased on master prior to testing. 2 threads: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 218863 W: 40927 L: 41153 D: 136783 http://tests.stockfishchess.org/tests/view/5d18c6c30ebc5925cf0b9566 4threads: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 16839 W: 3017 L: 2889 D: 10933 http://tests.stockfishchess.org/tests/view/5d18c6ea0ebc5925cf0b9568 No functional change. --- src/search.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 37fc2ec4..e008bec1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -102,6 +102,48 @@ namespace { Move best = MOVE_NONE; }; + // Breadcrumbs are used to mark nodes as being searched by a given thread. + struct Breadcrumb { + std::atomic thread; + std::atomic key; + }; + std::array breadcrumbs; + + // ThreadHolding keeps track of which thread left breadcrumbs at the given node for potential reductions. + // A free node will be marked upon entering the moves loop, and unmarked upon leaving that loop, by the ctor/dtor of this struct. + struct ThreadHolding { + explicit ThreadHolding(Thread* thisThread, Key posKey, int ply) { + location = ply < 8 ? &breadcrumbs[posKey & (breadcrumbs.size() - 1)] : nullptr; + otherThread = false; + owning = false; + if (location) + { + // see if another already marked this location, if not, mark it ourselves. + Thread* tmp = (*location).thread.load(std::memory_order_relaxed); + if (tmp == nullptr) + { + (*location).thread.store(thisThread, std::memory_order_relaxed); + (*location).key.store(posKey, std::memory_order_relaxed); + owning = true; + } + else if ( tmp != thisThread + && (*location).key.load(std::memory_order_relaxed) == posKey) + otherThread = true; + } + } + + ~ThreadHolding() { + if (owning) // free the marked location. + (*location).thread.store(nullptr, std::memory_order_relaxed); + } + + bool marked() { return otherThread; } + + private: + Breadcrumb* location; + bool otherThread, owning; + }; + template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); @@ -847,6 +889,9 @@ moves_loop: // When in check, search starts from here moveCountPruning = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); + // Mark this node as being searched. + ThreadHolding th(thisThread, posKey, ss->ply); + // Step 12. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) @@ -1019,6 +1064,10 @@ moves_loop: // When in check, search starts from here { Depth r = reduction(improving, depth, moveCount); + // Reduction if other threads are searching this position. + if (th.marked()) + r += ONE_PLY; + // Decrease reduction if position is or has been on the PV if (ttPv) r -= 2 * ONE_PLY; From ca51d1ee63f376e0eb6efb6f3d5d901e4b2a5bb0 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 1 Jul 2019 14:07:54 +0200 Subject: [PATCH 54/54] Smoothly change playing strength with skill level. (#2142) The current skill levels (1-20) allow for adjusting playing strengths, but do so in big steps (e.g. level 10 vs level 11 is a ~143 Elo jump at STC). Since the 'Skill Level' input can already be a floating point number, this patch uses the fractional part of the input to provide the user with fine control, allowing for varying the playing strength essentially continuously. The implementation internally still uses integer skill levels (needed since they pick Depths), but non-deterministically rounds up or down the used skill level such that the average integer skill corresponds to the input floating point one. As expected, intermediate (fractional) skill levels yield intermediate playing strenghts. Tested at STC, playing level 10 against levels between 10 and 11 for 10000 games level 10.25 ELO: 24.26 +-6.2 level 10.5 ELO: 67.51 +-6.3 level 10.75 ELO: 98.52 +-6.4 level 11 ELO: 143.65 +-6.7 http://tests.stockfishchess.org/tests/view/5cd9c6b40ebc5925cf056791 http://tests.stockfishchess.org/tests/view/5cd9d22b0ebc5925cf056989 http://tests.stockfishchess.org/tests/view/5cd9cf610ebc5925cf056906 http://tests.stockfishchess.org/tests/view/5cd9d2490ebc5925cf05698e No functional change. --- src/search.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e008bec1..b4a69092 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -335,7 +335,12 @@ void Thread::search() { beta = VALUE_INFINITE; multiPV = Options["MultiPV"]; - Skill skill(Options["Skill Level"]); + // Pick integer skill levels, but non-deterministically round up or down + // such that the average integer skill corresponds to the input floating point one. + PRNG rng(now()); + int intLevel = int(Options["Skill Level"]) + + ((Options["Skill Level"] - int(Options["Skill Level"])) * 1024 > rng.rand() % 1024 ? 1 : 0); + Skill skill(intLevel); // When playing with strength handicap enable MultiPV search that we will // use behind the scenes to retrieve a set of possible moves.