From 76d2f5b94a0df20d84ccf922bd1c0fcf1c779090 Mon Sep 17 00:00:00 2001 From: protonspring Date: Thu, 14 Feb 2019 14:23:24 -0700 Subject: [PATCH 01/17] Remove skipQuiets variable in search() This is a functional simplification. The moveCountPruning variable and the skipQuiets variable are similar enough in function that they can be combined. This removes the skipQuiets variable in search. STC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 23278 W: 5210 L: 5091 D: 12977 http://tests.stockfishchess.org/tests/view/5c65dc490ebc5925cffc12e9 LTC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 77107 W: 12792 L: 12761 D: 51554 http://tests.stockfishchess.org/tests/view/5c65e4360ebc5925cffc1490 Closes https://github.com/official-stockfish/Stockfish/pull/2011 bench 3640330 --- src/search.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c6b59b39..bbb6e5c4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -570,7 +570,7 @@ namespace { Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, pureStaticEval; bool ttHit, ttPv, inCheck, givesCheck, improving; - bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture; + bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount; @@ -880,12 +880,12 @@ moves_loop: // When in check, search starts from here ss->killers); value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc - skipQuiets = false; + moveCountPruning = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); // Step 12. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. - while ((move = mp.next_move(skipQuiets)) != MOVE_NONE) + while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) { assert(is_ok(move)); @@ -914,8 +914,9 @@ moves_loop: // When in check, search starts from here movedPiece = pos.moved_piece(move); givesCheck = gives_check(pos, move); - moveCountPruning = depth < 16 * ONE_PLY - && moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY]; + // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold + moveCountPruning = depth < 16 * ONE_PLY + && moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY]; // Step 13. Extensions (~70 Elo) @@ -973,10 +974,7 @@ moves_loop: // When in check, search starts from here { // Move count based pruning (~30 Elo) if (moveCountPruning) - { - skipQuiets = true; continue; - } // Reduced depth of the next LMR search int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO) / ONE_PLY; From 22ef36803ed0f1597d8f40731c6e2d3aa01debf2 Mon Sep 17 00:00:00 2001 From: protonspring Date: Wed, 13 Feb 2019 14:58:43 -0700 Subject: [PATCH 02/17] Remove PvNode dimension from Reductions array This is a functional simplification: if we simply subtract one to Reductions[] when PvNode is set, we can remove this dimension of the multidimensional array. I think this saves about 8K of memory. STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 10118 W: 2282 L: 2138 D: 5698 http://tests.stockfishchess.org/tests/view/5c6332b60ebc5925cffbdfed LTC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 70765 W: 11617 L: 11575 D: 47573 http://tests.stockfishchess.org/tests/view/5c63379e0ebc5925cffbe0de Closes https://github.com/official-stockfish/Stockfish/pull/2010 Bench 3261078 --- src/search.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index bbb6e5c4..dbfb9469 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -73,10 +73,10 @@ namespace { // Futility and reductions lookup tables, initialized at startup int FutilityMoveCounts[2][16]; // [improving][depth] - int Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber] + int Reductions[2][64][64]; // [improving][depth][moveNumber] template Depth reduction(bool i, Depth d, int mn) { - return Reductions[PvNode][i][std::min(d / ONE_PLY, 63)][std::min(mn, 63)] * ONE_PLY; + return (Reductions[i][std::min(d / ONE_PLY, 63)][std::min(mn, 63)] - PvNode) * ONE_PLY; } // History and stats update bonus, based on depth @@ -162,12 +162,11 @@ void Search::init() { { double r = log(d) * log(mc) / 1.95; - Reductions[NonPV][imp][d][mc] = int(std::round(r)); - Reductions[PV][imp][d][mc] = std::max(Reductions[NonPV][imp][d][mc] - 1, 0); + Reductions[imp][d][mc] = std::round(r); // Increase reduction for non-PV nodes when eval is not improving if (!imp && r > 1.0) - Reductions[NonPV][imp][d][mc]++; + Reductions[imp][d][mc]++; } for (int d = 0; d < 16; ++d) From e51244cb27151075ac7a05282d0ec615cf93f290 Mon Sep 17 00:00:00 2001 From: MJZ1977 <37274752+MJZ1977@users.noreply.github.com> Date: Tue, 19 Feb 2019 17:48:03 +0100 Subject: [PATCH 03/17] ProbCutCount limit dependancy to cutNode Use the ProbCutCount limit `2 + 2 * cutNode` instead of constant 3. STC LLR: -2.95 (-2.94,2.94) [0.50,4.50] Total: 61812 W: 13599 L: 13459 D: 34754 http://tests.stockfishchess.org/tests/view/5c6d19240ebc5925cffca07a LTC LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 27549 W: 4614 L: 4363 D: 18572 http://tests.stockfishchess.org/tests/view/5c6d45c10ebc5925cffca7a6 Closes https://github.com/official-stockfish/Stockfish/pull/2015 Bench: 3368889 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index dbfb9469..6bb1e1df 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -830,7 +830,7 @@ namespace { int probCutCount = 0; while ( (move = mp.next_move()) != MOVE_NONE - && probCutCount < 3) + && probCutCount < 2 + 2 * cutNode) if (move != excludedMove && pos.legal(move)) { probCutCount++; From badb2aca44d6507f35dafc8b5c3921a6649a40f8 Mon Sep 17 00:00:00 2001 From: Kurtbusch Date: Fri, 4 Jan 2019 18:27:14 -0500 Subject: [PATCH 04/17] Add KNNvKP Endgame Heuristic This is a somewhat different patch. It fixes blindspots for two knights vs pawn endgame. With local testing starting from random KNNvKP positions where the pawn has not advanced beyond the 4th rank (thanks @protonspring !) at 15+0.15 (4 cores), this went +105=868-27 against master. All except two losses were won in reverse. The heuristic is simple but effective - the strategy in these endgames is to push the opposing king to the corner, then move the knight that's blocking the pawn in for the checkmate while the pawn is free to move and prevents stalemate. This patch gives SF the little boost it needs to search the relevant king-cornering mating lines. See the discussion in pull request 1939 for some more good results for this test in independant tests: https://github.com/official-stockfish/Stockfish/pull/1939 Bench: 3310239 --- src/endgame.cpp | 15 +++++++++++++++ src/endgame.h | 2 ++ src/search.cpp | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 835173c4..3e572205 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -286,6 +286,21 @@ Value Endgame::operator()(const Position& pos) const { } +/// KNN vs KP. Simply push the opposing king to the corner. +template<> +Value Endgame::operator()(const Position& pos) const { + + assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); + + Value result = 2 * KnightValueEg + - PawnValueEg + + PushToEdges[pos.square(weakSide)]; + + return strongSide == pos.side_to_move() ? result : -result; +} + + /// Some cases of trivial draws template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } diff --git a/src/endgame.h b/src/endgame.h index 941cb310..2a48488f 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -37,6 +37,7 @@ enum EndgameCode { EVALUATION_FUNCTIONS, KNNK, // KNN vs K + KNNKP, // KNN vs KP KXK, // Generic "mate lone king" eval KBNK, // KBN vs K KPK, // KP vs K @@ -125,6 +126,7 @@ public: add("KRKN"); add("KQKP"); add("KQKR"); + add("KNNKP"); add("KNPK"); add("KNPKB"); diff --git a/src/search.cpp b/src/search.cpp index 6bb1e1df..2e7dd698 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -830,7 +830,7 @@ namespace { int probCutCount = 0; while ( (move = mp.next_move()) != MOVE_NONE - && probCutCount < 2 + 2 * cutNode) + && probCutCount < 2 + 2 * cutNode) if (move != excludedMove && pos.legal(move)) { probCutCount++; From 7c5f5fbadabb9c163443cfe05e6a522495607fef Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Tue, 26 Feb 2019 13:20:48 +0100 Subject: [PATCH 05/17] Revert "Remove skipQuiets variable in search()" This reverts commit 76d2f5b94a0df20d84ccf922bd1c0fcf1c779090. Due to a bug, see https://github.com/official-stockfish/Stockfish/issues/2019 Bench: 3516616 --- src/search.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2e7dd698..98c8e9cb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -569,7 +569,7 @@ namespace { Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, pureStaticEval; bool ttHit, ttPv, inCheck, givesCheck, improving; - bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; + bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount; @@ -879,12 +879,12 @@ moves_loop: // When in check, search starts from here ss->killers); value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc - moveCountPruning = false; + skipQuiets = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); // 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) + while ((move = mp.next_move(skipQuiets)) != MOVE_NONE) { assert(is_ok(move)); @@ -913,9 +913,8 @@ moves_loop: // When in check, search starts from here movedPiece = pos.moved_piece(move); givesCheck = gives_check(pos, move); - // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold - moveCountPruning = depth < 16 * ONE_PLY - && moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY]; + moveCountPruning = depth < 16 * ONE_PLY + && moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY]; // Step 13. Extensions (~70 Elo) @@ -973,7 +972,10 @@ moves_loop: // When in check, search starts from here { // Move count based pruning (~30 Elo) if (moveCountPruning) + { + skipQuiets = true; continue; + } // Reduced depth of the next LMR search int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO) / ONE_PLY; From 82ff04b992a53c757519a6ff61576ebd267c0cee Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 27 Feb 2019 13:36:48 +0100 Subject: [PATCH 06/17] Remove skipQuiets with mate fix. (#2021) This removes the skipQuiets variable, as was done in an earlier round by @protonspring, but fixes an oversight which led to wrong mate announcements. Quiets can only be pruned when there is no mate score, so set moveCountPruning at the right spot. tested as a fix at STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 66321 W: 14690 L: 14657 D: 36974 http://tests.stockfishchess.org/tests/view/5c74f3170ebc5925cffd4b3c and as the full patch at LTC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 25903 W: 4341 L: 4228 D: 17334 http://tests.stockfishchess.org/tests/view/5c7540030ebc5925cffd506f Bench: 3292342 --- src/search.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 98c8e9cb..d3f1335b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -569,7 +569,7 @@ namespace { Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, pureStaticEval; bool ttHit, ttPv, inCheck, givesCheck, improving; - bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture; + bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount; @@ -879,12 +879,12 @@ moves_loop: // When in check, search starts from here ss->killers); value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc - skipQuiets = false; + moveCountPruning = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); // Step 12. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. - while ((move = mp.next_move(skipQuiets)) != MOVE_NONE) + while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) { assert(is_ok(move)); @@ -913,9 +913,6 @@ moves_loop: // When in check, search starts from here movedPiece = pos.moved_piece(move); givesCheck = gives_check(pos, move); - moveCountPruning = depth < 16 * ONE_PLY - && moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY]; - // Step 13. Extensions (~70 Elo) // Singular extension search (~60 Elo). If all moves but one fail low on a @@ -966,16 +963,17 @@ moves_loop: // When in check, search starts from here && pos.non_pawn_material(us) && bestValue > VALUE_MATED_IN_MAX_PLY) { + // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold + moveCountPruning = depth < 16 * ONE_PLY + && moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY]; + if ( !captureOrPromotion && !givesCheck && !pos.advanced_pawn_push(move)) { // Move count based pruning (~30 Elo) if (moveCountPruning) - { - skipQuiets = true; continue; - } // Reduced depth of the next LMR search int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO) / ONE_PLY; From c2fb0ff7204ab217a053175c41d24afb9e689a75 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 27 Feb 2019 23:25:12 +0300 Subject: [PATCH 07/17] Add continuation history 5 Original patch passed STC: http://tests.stockfishchess.org/tests/view/5c7439ff0ebc5925cffd3e64 LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 26348 W: 5926 L: 5632 D: 14790 and LTC: http://tests.stockfishchess.org/tests/view/5c745a8b0ebc5925cffd41a8 LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 198411 W: 33238 L: 32510 D: 132663 But had undefined behavior. After fixing (thx to @vondele ) passed LTC: http://tests.stockfishchess.org/tests/view/5c763c7c0ebc5925cffd5de2 LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 112253 W: 18711 L: 18225 D: 75317 bench 3049229 --- src/movepick.cpp | 3 ++- src/search.cpp | 16 ++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 87c3f6fa..86eb98aa 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -114,7 +114,8 @@ void MovePicker::score() { m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] - + (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]; + + (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)] + + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)] / 2; else // Type == EVASIONS { diff --git a/src/search.cpp b/src/search.cpp index d3f1335b..e75d085b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -301,7 +301,7 @@ void Thread::search() { // The former is needed to allow update_continuation_histories(ss-1, ...), // which accesses its argument at ss-4, also near the root. // The latter is needed for statScores and killer initialization. - Stack stack[MAX_PLY+8], *ss = stack+5; + Stack stack[MAX_PLY+10], *ss = stack+7; Move pv[MAX_PLY+1]; Value bestValue, alpha, beta, delta; Move lastBestMove = MOVE_NONE; @@ -311,8 +311,8 @@ void Thread::search() { Color us = rootPos.side_to_move(); bool failedLow; - std::memset(ss-5, 0, 8 * sizeof(Stack)); - for (int i = 5; i > 0; i--) + std::memset(ss-7, 0, 10 * sizeof(Stack)); + for (int i = 7; i > 0; i--) (ss-i)->continuationHistory = &this->continuationHistory[NO_PIECE][0]; // Use as sentinel ss->pv = pv; @@ -869,7 +869,9 @@ namespace { moves_loop: // When in check, search starts from here - const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, nullptr, (ss-4)->continuationHistory }; + const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, + nullptr, (ss-4)->continuationHistory, + nullptr, (ss-6)->continuationHistory }; Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq]; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, @@ -1320,7 +1322,9 @@ moves_loop: // When in check, search starts from here futilityBase = bestValue + 128; } - const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, nullptr, (ss-4)->continuationHistory }; + const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, + nullptr, (ss-4)->continuationHistory, + nullptr, (ss-6)->continuationHistory }; // Initialize a MovePicker object for the current position, and prepare // to search the moves. Because the depth is <= 0 here, only captures, @@ -1470,7 +1474,7 @@ moves_loop: // When in check, search starts from here void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { - for (int i : {1, 2, 4}) + for (int i : {1, 2, 4, 6}) if (is_ok((ss-i)->currentMove)) (*(ss-i)->continuationHistory)[pc][to] << bonus; } From 2bceba7f5162198834ca9f3dca0258e7eac1f797 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Fri, 8 Feb 2019 10:36:03 +0100 Subject: [PATCH 08/17] Assorted trivial cleanups 2/2019 No functional change. --- src/bitboard.cpp | 27 +++++++++--------- src/bitboard.h | 45 +++++++++++++++++------------- src/evaluate.cpp | 63 +++++++++++++++++------------------------- src/movegen.cpp | 11 +++----- src/pawns.cpp | 2 +- src/position.h | 2 +- src/search.cpp | 2 +- src/syzygy/tbprobe.cpp | 2 +- src/syzygy/tbprobe.h | 2 +- src/ucioption.cpp | 20 +++++++++++--- 10 files changed, 90 insertions(+), 86 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 13735e3d..d90201d3 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -24,7 +24,7 @@ #include "misc.h" uint8_t PopCnt16[1 << 16]; -int8_t SquareDistance[SQUARE_NB][SQUARE_NB]; +uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; Bitboard SquareBB[SQUARE_NB]; Bitboard ForwardRanksBB[COLOR_NB][RANK_NB]; @@ -34,6 +34,12 @@ Bitboard DistanceRingBB[SQUARE_NB][8]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; +Bitboard KingFlank[FILE_NB] = { + QueenSide ^ FileDBB, QueenSide, QueenSide, + CenterFiles, CenterFiles, + KingSide, KingSide, KingSide ^ FileEBB +}; + Magic RookMagics[SQUARE_NB]; Magic BishopMagics[SQUARE_NB]; @@ -45,7 +51,6 @@ namespace { void init_magics(Bitboard table[], Magic magics[], Direction directions[]); // popcount16() counts the non-zero bits using SWAR-Popcount algorithm - unsigned popcount16(unsigned u) { u -= (u >> 1) & 0x5555U; u = ((u >> 2) & 0x3333U) + (u & 0x3333U); @@ -80,17 +85,13 @@ const std::string Bitboards::pretty(Bitboard b) { void Bitboards::init() { for (unsigned i = 0; i < (1 << 16); ++i) - PopCnt16[i] = (uint8_t) popcount16(i); + PopCnt16[i] = (uint8_t)popcount16(i); for (Square s = SQ_A1; s <= SQ_H8; ++s) SquareBB[s] = (1ULL << s); - for (Rank r = RANK_1; r < RANK_8; ++r) - ForwardRanksBB[WHITE][r] = ~(ForwardRanksBB[BLACK][r + 1] = ForwardRanksBB[BLACK][r] | rank_bb(r)); - for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) - if (s1 != s2) { SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); DistanceRingBB[s1][SquareDistance[s1][s2]] |= s2; @@ -127,13 +128,11 @@ void Bitboards::init() { for (PieceType pt : { BISHOP, ROOK }) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) - { - if (!(PseudoAttacks[pt][s1] & s2)) - continue; - - LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; - BetweenBB[s1][s2] = attacks_bb(pt, s1, SquareBB[s2]) & attacks_bb(pt, s2, SquareBB[s1]); - } + if (PseudoAttacks[pt][s1] & s2) + { + LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; + BetweenBB[s1][s2] = attacks_bb(pt, s1, SquareBB[s2]) & attacks_bb(pt, s2, SquareBB[s1]); + } } } diff --git a/src/bitboard.h b/src/bitboard.h index cf52b39d..af7b592a 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -60,15 +60,21 @@ constexpr Bitboard Rank6BB = Rank1BB << (8 * 5); constexpr Bitboard Rank7BB = Rank1BB << (8 * 6); constexpr Bitboard Rank8BB = Rank1BB << (8 * 7); -extern int8_t SquareDistance[SQUARE_NB][SQUARE_NB]; +constexpr Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB; +constexpr Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB; +constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB; +constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB); + +extern uint8_t PopCnt16[1 << 16]; +extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; extern Bitboard SquareBB[SQUARE_NB]; -extern Bitboard ForwardRanksBB[COLOR_NB][RANK_NB]; extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard DistanceRingBB[SQUARE_NB][8]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; +extern Bitboard KingFlank[FILE_NB]; /// Magic holds all magic bitboards relevant data for a single square @@ -133,6 +139,7 @@ inline bool opposite_colors(Square s1, Square s2) { return bool(DarkSquares & s1) != bool(DarkSquares & s2); } + /// rank_bb() and file_bb() return a bitboard representing all the squares on /// the given file or rank. @@ -192,48 +199,49 @@ inline Bitboard adjacent_files_bb(File f) { return shift(file_bb(f)) | shift(file_bb(f)); } + /// between_bb() returns a bitboard representing all the squares between the two /// given ones. For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with -/// the bits for square d5 and e6 set. If s1 and s2 are not on the same rank, file -/// or diagonal, 0 is returned. +/// the bits for square d5 and e6 set. If s1 and s2 are not on the same rank, +/// file or diagonal, 0 is returned. inline Bitboard between_bb(Square s1, Square s2) { return BetweenBB[s1][s2]; } -/// forward_ranks_bb() returns a bitboard representing the squares on all the ranks +/// forward_ranks_bb() returns a bitboard representing the squares on the ranks /// in front of the given one, from the point of view of the given color. For instance, /// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2. inline Bitboard forward_ranks_bb(Color c, Square s) { - return ForwardRanksBB[c][rank_of(s)]; + return c == WHITE ? ~Rank1BB << 8 * (rank_of(s) - RANK_1) + : ~Rank8BB >> 8 * (RANK_8 - rank_of(s)); } -/// forward_file_bb() returns a bitboard representing all the squares along the line -/// in front of the given one, from the point of view of the given color: -/// ForwardFileBB[c][s] = forward_ranks_bb(c, s) & file_bb(s) +/// forward_file_bb() returns a bitboard representing all the squares along the +/// line in front of the given one, from the point of view of the given color. inline Bitboard forward_file_bb(Color c, Square s) { - return ForwardRanksBB[c][rank_of(s)] & file_bb(s); + return forward_ranks_bb(c, s) & file_bb(s); } -/// pawn_attack_span() returns a bitboard representing all the squares that can be -/// attacked by a pawn of the given color when it moves along its file, starting -/// from the given square: +/// pawn_attack_span() returns a bitboard representing all the squares that can +/// be attacked by a pawn of the given color when it moves along its file, +/// 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)); } -/// passed_pawn_mask() returns a bitboard mask which can be used to test if a -/// pawn of the given color and on the given square is a passed pawn: +/// passed_pawn_span() returns a bitboard which can be used to test if a pawn of +/// the given color and on the given square is a passed pawn. -inline Bitboard passed_pawn_mask(Color c, Square s) { - return pawn_attack_span(c, s) | forward_file_bb(c, s); +inline Bitboard passed_pawn_span(Color c, Square s) { + return forward_ranks_bb(c, s) & (adjacent_files_bb(file_of(s)) | file_bb(s)); } @@ -248,7 +256,7 @@ inline bool aligned(Square s1, Square s2, Square s3) { /// distance() functions return the distance between x and y, defined as the /// number of steps for a king in x to reach y. Works with squares, ranks, files. -template inline int distance(T x, T y) { return x < y ? y - x : x - y; } +template inline int distance(T x, T y) { return std::abs(x - y); } template<> inline int distance(Square x, Square y) { return SquareDistance[x][y]; } template inline int distance(T2 x, T2 y); @@ -286,7 +294,6 @@ inline int popcount(Bitboard b) { #ifndef USE_POPCNT - extern uint8_t PopCnt16[1 << 16]; union { Bitboard bb; uint16_t u[4]; } v = { b }; return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]]; diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 98082c4c..27e1dd3d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -73,17 +73,6 @@ using namespace Trace; namespace { - constexpr Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB; - 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 - }; - // Threshold for lazy and space evaluation constexpr Value LazyThreshold = Value(1500); constexpr Value SpaceThreshold = Value(12222); @@ -152,6 +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 PawnlessFlank = S( 17, 95); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RookOnPawn = S( 10, 32); @@ -163,7 +153,6 @@ namespace { constexpr Score TrappedRook = S( 47, 4); constexpr Score WeakQueen = S( 49, 15); constexpr Score WeakUnopposedPawn = S( 12, 23); - constexpr Score Outpost = S( 9, 3); #undef S @@ -402,7 +391,8 @@ namespace { constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB : AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB); - Bitboard weak, b, b1, b2, safe, unsafeChecks = 0; + Bitboard weak, b1, b2, safe, unsafeChecks = 0; + Bitboard rookChecks, queenChecks, bishopChecks, knightChecks; int kingDanger = 0; const Square ksq = pos.square(Us); @@ -422,45 +412,43 @@ namespace { b2 = attacks_bb(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)); // Enemy rooks checks - Bitboard RookCheck = b1 - & safe - & attackedBy[Them][ROOK]; + rookChecks = b1 & safe & attackedBy[Them][ROOK]; - if (RookCheck) + if (rookChecks) kingDanger += RookSafeCheck; else unsafeChecks |= b1 & attackedBy[Them][ROOK]; // Enemy queen safe checks: we count them only if they are from squares from // which we can't give a rook check, because rook checks are more valuable. - Bitboard QueenCheck = (b1 | b2) - & attackedBy[Them][QUEEN] - & safe - & ~attackedBy[Us][QUEEN] - & ~RookCheck; + queenChecks = (b1 | b2) + & attackedBy[Them][QUEEN] + & safe + & ~attackedBy[Us][QUEEN] + & ~rookChecks; - if (QueenCheck) + if (queenChecks) kingDanger += QueenSafeCheck; // Enemy bishops checks: we count them only if they are from squares from // which we can't give a queen check, because queen checks are more valuable. - Bitboard BishopCheck = b2 - & attackedBy[Them][BISHOP] - & safe - & ~QueenCheck; + bishopChecks = b2 + & attackedBy[Them][BISHOP] + & safe + & ~queenChecks; - if (BishopCheck) + if (bishopChecks) kingDanger += BishopSafeCheck; else unsafeChecks |= b2 & attackedBy[Them][BISHOP]; // Enemy knights checks - b = pos.attacks_from(ksq) & attackedBy[Them][KNIGHT]; + knightChecks = pos.attacks_from(ksq) & attackedBy[Them][KNIGHT]; - if (b & safe) + if (knightChecks & safe) kingDanger += KnightSafeCheck; else - unsafeChecks |= b; + unsafeChecks |= knightChecks; // Unsafe or occupied checking squares will also be considered, as long as // the square is in the attacker's mobility area. @@ -511,7 +499,7 @@ namespace { constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); - Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe, restricted; + Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe; Score score = SCORE_ZERO; // Non-pawn enemies @@ -561,10 +549,11 @@ namespace { } // Bonus for restricting their piece moves - restricted = attackedBy[Them][ALL_PIECES] - & ~stronglyProtected - & attackedBy[Us][ALL_PIECES]; - score += RestrictedPiece * popcount(restricted); + b = attackedBy[Them][ALL_PIECES] + & ~stronglyProtected + & attackedBy[Us][ALL_PIECES]; + + score += RestrictedPiece * popcount(b); // Bonus for enemy unopposed weak pawns if (pos.pieces(Us, ROOK, QUEEN)) @@ -901,7 +890,6 @@ std::string Eval::trace(const Position& pos) { << " ------------+-------------+-------------+------------\n" << " Material | " << Term(MATERIAL) << " Imbalance | " << Term(IMBALANCE) - << " Initiative | " << Term(INITIATIVE) << " Pawns | " << Term(PAWN) << " Knights | " << Term(KNIGHT) << " Bishops | " << Term(BISHOP) @@ -912,6 +900,7 @@ std::string Eval::trace(const Position& pos) { << " Threats | " << Term(THREAT) << " Passed | " << Term(PASSED) << " Space | " << Term(SPACE) + << " Initiative | " << Term(INITIATIVE) << " ------------+-------------+-------------+------------\n" << " Total | " << Term(TOTAL); diff --git a/src/movegen.cpp b/src/movegen.cpp index 2f7c55c8..4c609352 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -252,14 +252,11 @@ namespace { } // namespace -/// generate generates all pseudo-legal captures and queen -/// promotions. Returns a pointer to the end of the move list. +/// Generates all pseudo-legal captures and queen promotions +/// Generates all pseudo-legal non-captures and underpromotions +/// Generates all pseudo-legal captures and non-captures /// -/// generate generates all pseudo-legal non-captures and -/// underpromotions. Returns a pointer to the end of the move list. -/// -/// generate generates all pseudo-legal captures and -/// non-captures. Returns a pointer to the end of the move list. +/// Returns a pointer to the end of the move list. template ExtMove* generate(const Position& pos, ExtMove* moveList) { diff --git a/src/pawns.cpp b/src/pawns.cpp index c581c8e7..6072745b 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -96,7 +96,7 @@ namespace { // Flag the pawn opposed = theirPawns & forward_file_bb(Us, s); - stoppers = theirPawns & passed_pawn_mask(Us, s); + stoppers = theirPawns & passed_pawn_span(Us, s); lever = theirPawns & PawnAttacks[Us][s]; leverPush = theirPawns & PawnAttacks[Us][s + Up]; doubled = ourPawns & (s - Up); diff --git a/src/position.h b/src/position.h index 74173d03..b65522b7 100644 --- a/src/position.h +++ b/src/position.h @@ -310,7 +310,7 @@ inline Bitboard Position::check_squares(PieceType pt) const { } inline bool Position::pawn_passed(Color c, Square s) const { - return !(pieces(~c, PAWN) & passed_pawn_mask(c, s)); + return !(pieces(~c, PAWN) & passed_pawn_span(c, s)); } inline bool Position::advanced_pawn_push(Move m) const { diff --git a/src/search.cpp b/src/search.cpp index e75d085b..23402939 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -845,7 +845,7 @@ namespace { // Perform a preliminary qsearch to verify that the move holds value = -qsearch(pos, ss+1, -raisedBeta, -raisedBeta+1); - // If the qsearch held perform the regular search + // If the qsearch held, perform the regular search if (value >= raisedBeta) value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4 * ONE_PLY, !cutNode); diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index bf6ed421..01bbc7ef 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1,7 +1,7 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (c) 2013 Ronald de Man - Copyright (C) 2016-2018 Marco Costalba, Lucas Braesch + Copyright (C) 2016-2019 Marco Costalba, Lucas Braesch Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index 572265b5..264f6e84 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -1,7 +1,7 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (c) 2013 Ronald de Man - Copyright (C) 2016-2018 Marco Costalba, Lucas Braesch + Copyright (C) 2016-2019 Marco Costalba, Lucas Braesch Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 1c6ef777..813a0890 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "misc.h" #include "search.h" @@ -136,8 +137,8 @@ Option::operator std::string() const { bool Option::operator==(const char* s) const { assert(type == "combo"); - return !CaseInsensitiveLess()(currentValue, s) - && !CaseInsensitiveLess()(s, currentValue); + return !CaseInsensitiveLess()(currentValue, s) + && !CaseInsensitiveLess()(s, currentValue); } @@ -153,8 +154,8 @@ void Option::operator<<(const Option& o) { /// operator=() updates currentValue and triggers on_change() action. It's up to -/// the GUI to check for option's limits, but we could receive the new value from -/// the user by console window, so let's check the bounds anyway. +/// the GUI to check for option's limits, but we could receive the new value +/// from the user by console window, so let's check the bounds anyway. Option& Option::operator=(const string& v) { @@ -165,6 +166,17 @@ Option& Option::operator=(const string& v) { || (type == "spin" && (stof(v) < min || stof(v) > max))) return *this; + if (type == "combo") + { + OptionsMap comboMap; // To have case insensitive compare + string token; + std::istringstream ss(defaultValue); + while (ss >> token) + comboMap[token] << Option(); + if (!comboMap.count(v) || v == "var") + return *this; + } + if (type != "button") currentValue = v; From 58bbbd176b81edb7137ee6314e4d5bf9f208106d Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Tue, 5 Mar 2019 12:34:02 +0800 Subject: [PATCH 09/17] Pawn value tweak STC: LLR: 2.96 (-2.94,2.94) [0.00,4.00] Total: 47166 W: 10664 L: 10311 D: 26191 http://tests.stockfishchess.org/tests/view/5c7dfc370ebc5925cffdf830 LTC: LLR: 2.95 (-2.94,2.94) [0.00,4.00] Total: 35439 W: 6034 L: 5767 D: 23638 http://tests.stockfishchess.org/tests/view/5c7e41020ebc5925cffdfe9b Bench: 3470519 --- src/types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.h b/src/types.h index a7415519..2e36279d 100644 --- a/src/types.h +++ b/src/types.h @@ -180,7 +180,7 @@ enum Value : int { VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY, - PawnValueMg = 136, PawnValueEg = 208, + PawnValueMg = 128, PawnValueEg = 213, KnightValueMg = 782, KnightValueEg = 865, BishopValueMg = 830, BishopValueEg = 918, RookValueMg = 1289, RookValueEg = 1378, From 714e857c246879a513cdf0d3d3fe757a42d7030a Mon Sep 17 00:00:00 2001 From: protonspring Date: Sun, 3 Mar 2019 07:53:36 -0700 Subject: [PATCH 10/17] Shrink Reductions[] array to one dimension This is a non-functional patch which shrinks the reductions array. This saves about 8Kb of memory. The only slow part of master's reductions array is the calculation of the log values, so using a separate array for those values and calculating the rest real-time appears to be just as fast as master. STC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 63245 W: 13906 L: 13866 D: 35473 http://tests.stockfishchess.org/tests/view/5c7b571f0ebc5925cffdc104 No funcional change. --- src/search.cpp | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 23402939..39875cb4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -73,10 +73,11 @@ namespace { // Futility and reductions lookup tables, initialized at startup int FutilityMoveCounts[2][16]; // [improving][depth] - int Reductions[2][64][64]; // [improving][depth][moveNumber] + int Reductions[64]; // [depth or moveNumber] template Depth reduction(bool i, Depth d, int mn) { - return (Reductions[i][std::min(d / ONE_PLY, 63)][std::min(mn, 63)] - PvNode) * ONE_PLY; + int r = Reductions[std::min(d / ONE_PLY, 63)] * Reductions[std::min(mn, 63)] / 1024; + return ((r + 512) / 1024 + (!i && r > 1024) - PvNode) * ONE_PLY; } // History and stats update bonus, based on depth @@ -156,18 +157,8 @@ namespace { void Search::init() { - for (int imp = 0; imp <= 1; ++imp) - for (int d = 1; d < 64; ++d) - for (int mc = 1; mc < 64; ++mc) - { - double r = log(d) * log(mc) / 1.95; - - Reductions[imp][d][mc] = std::round(r); - - // Increase reduction for non-PV nodes when eval is not improving - if (!imp && r > 1.0) - Reductions[imp][d][mc]++; - } + for (int i = 1; i < 64; ++i) + Reductions[i] = int(1024 * std::log(i) / std::sqrt(1.95)); for (int d = 0; d < 16; ++d) { From 1aab5b4b055dffae5e2a2cf9958d3d0d79f66cb4 Mon Sep 17 00:00:00 2001 From: protonspring Date: Tue, 5 Mar 2019 12:48:29 -0700 Subject: [PATCH 11/17] Remove FutilityMoveCounts array. (#2024) This is a functional simplification that removes the FutilityMoveCounts array with a simple equation using only ints. LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 14175 W: 3123 L: 2987 D: 8065 LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 9900 W: 1735 L: 1597 D: 6568 Bench: 3380343 --- src/search.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 39875cb4..31ae5377 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -71,8 +71,7 @@ namespace { return Value((175 - 50 * improving) * d / ONE_PLY); } - // Futility and reductions lookup tables, initialized at startup - int FutilityMoveCounts[2][16]; // [improving][depth] + // Reductions lookup table, initialized at startup int Reductions[64]; // [depth or moveNumber] template Depth reduction(bool i, Depth d, int mn) { @@ -80,6 +79,10 @@ namespace { return ((r + 512) / 1024 + (!i && r > 1024) - PvNode) * ONE_PLY; } + constexpr int futility_move_count(bool improving, int depth) { + return (5 + depth * depth) * (1 + improving) / 2; + } + // History and stats update bonus, based on depth int stat_bonus(Depth depth) { int d = depth / ONE_PLY; @@ -159,12 +162,6 @@ void Search::init() { for (int i = 1; i < 64; ++i) Reductions[i] = int(1024 * std::log(i) / std::sqrt(1.95)); - - for (int d = 0; d < 16; ++d) - { - FutilityMoveCounts[0][d] = int(2.4 + 0.74 * pow(d, 1.78)); - FutilityMoveCounts[1][d] = int(5.0 + 1.00 * pow(d, 2.00)); - } } @@ -957,8 +954,7 @@ moves_loop: // When in check, search starts from here && bestValue > VALUE_MATED_IN_MAX_PLY) { // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold - moveCountPruning = depth < 16 * ONE_PLY - && moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY]; + moveCountPruning = moveCount >= futility_move_count(improving,depth / ONE_PLY); if ( !captureOrPromotion && !givesCheck From 4d0981fef37c85edfd7a0e713d68a4542d7a6784 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sat, 9 Mar 2019 13:24:26 +0100 Subject: [PATCH 12/17] Revert "Allowing singular extension in mate positions" It was causing an assert: value > -VALUE_INFINITE under some conditions. See https://github.com/official-stockfish/Stockfish/issues/2036 Bench: 3318033 --- src/search.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 31ae5377..9165468a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -914,12 +914,13 @@ moves_loop: // When in check, search starts from here && move == ttMove && !rootNode && !excludedMove // Avoid recursive singular search - && ttValue != VALUE_NONE + /* && ttValue != VALUE_NONE Already implicit in the next condition */ + && abs(ttValue) < VALUE_KNOWN_WIN && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3 * ONE_PLY && pos.legal(move)) { - Value singularBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE); + Value singularBeta = ttValue - 2 * depth / ONE_PLY; ss->excludedMove = move; value = search(pos, ss, singularBeta - 1, singularBeta, depth / 2, cutNode); ss->excludedMove = MOVE_NONE; From acc47e8b79058a06508fee804aa428820e163b8b Mon Sep 17 00:00:00 2001 From: xoto10 Date: Fri, 1 Feb 2019 20:02:03 +0000 Subject: [PATCH 13/17] Simplify failedLow away #1986 FailedLow doesn't seem to add any value so remove it. STC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 43915 W: 9682 L: 9604 D: 24629 http://tests.stockfishchess.org/tests/view/5c5339770ebc592fc7baef74 LTC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 58515 W: 9670 L: 9609 D: 39236 http://tests.stockfishchess.org/tests/view/5c53cc840ebc592fc7baf6c1 Ideas for further work: Tune the values in the revised fallingEval calculation Consider adding a term using delta, e.g. c * (delta - 20) as an indicator of eval instability Bench: 3318033 --- src/search.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9165468a..ba1fef4d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -297,7 +297,6 @@ void Thread::search() { MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); double timeReduction = 1.0; Color us = rootPos.side_to_move(); - bool failedLow; std::memset(ss-7, 0, 10 * sizeof(Stack)); for (int i = 7; i > 0; i--) @@ -308,7 +307,7 @@ void Thread::search() { beta = VALUE_INFINITE; if (mainThread) - mainThread->bestMoveChanges = 0, failedLow = false; + mainThread->bestMoveChanges = 0; size_t multiPV = Options["MultiPV"]; Skill skill(Options["Skill Level"]); @@ -349,7 +348,7 @@ void Thread::search() { // Age out PV variability metric if (mainThread) - mainThread->bestMoveChanges *= 0.517, failedLow = false; + mainThread->bestMoveChanges *= 0.517; // Save the last iteration's scores before first PV line is searched and // all the move scores except the (new) PV are set to -VALUE_INFINITE. @@ -429,7 +428,6 @@ void Thread::search() { if (mainThread) { failedHighCnt = 0; - failedLow = true; mainThread->stopOnPonderhit = false; } } @@ -481,19 +479,19 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (306 + 119 * failedLow + 6 * (mainThread->previousScore - bestValue)) / 581.0; + double fallingEval = (306 + 9 * (mainThread->previousScore - bestValue)) / 581.0; fallingEval = std::max(0.5, std::min(1.5, fallingEval)); // If the bestMove is stable over several iterations, reduce time accordingly timeReduction = lastBestMoveDepth + 10 * ONE_PLY < completedDepth ? 1.95 : 1.0; + double reduction = std::pow(mainThread->previousTimeReduction, 0.528) / timeReduction; // Use part of the gained time from a previous stable move for the current move double bestMoveInstability = 1.0 + mainThread->bestMoveChanges; - bestMoveInstability *= std::pow(mainThread->previousTimeReduction, 0.528) / timeReduction; // Stop the search if we have only one legal move, or if available time elapsed if ( rootMoves.size() == 1 - || Time.elapsed() > Time.optimum() * bestMoveInstability * fallingEval) + || Time.elapsed() > Time.optimum() * fallingEval * reduction * bestMoveInstability) { // If we are allowed to ponder do not stop the search now but // keep pondering until the GUI sends "ponderhit" or "stop". From b8efa0daac897e1b8f3efb9ca09ec4151492f1e0 Mon Sep 17 00:00:00 2001 From: protonspring Date: Sun, 10 Mar 2019 03:53:39 -0600 Subject: [PATCH 14/17] Remove popcount16() (#2038) This is a non-functional simplification / code-style change. This popcount16 method does nothing but initialize the PopCnt16 arrays. This can be done in a single bitset line, which is less lines and more clear. Performance for this code is moot. No functional change. --- src/bitboard.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index d90201d3..f66d971b 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -19,6 +19,7 @@ */ #include +#include #include "bitboard.h" #include "misc.h" @@ -49,14 +50,6 @@ namespace { Bitboard BishopTable[0x1480]; // To store bishop attacks void init_magics(Bitboard table[], Magic magics[], Direction directions[]); - - // popcount16() counts the non-zero bits using SWAR-Popcount algorithm - unsigned popcount16(unsigned u) { - u -= (u >> 1) & 0x5555U; - u = ((u >> 2) & 0x3333U) + (u & 0x3333U); - u = ((u >> 4) + u) & 0x0F0FU; - return (u * 0x0101U) >> 8; - } } @@ -85,7 +78,7 @@ const std::string Bitboards::pretty(Bitboard b) { void Bitboards::init() { for (unsigned i = 0; i < (1 << 16); ++i) - PopCnt16[i] = (uint8_t)popcount16(i); + PopCnt16[i] = std::bitset<16>(i).count(); for (Square s = SQ_A1; s <= SQ_H8; ++s) SquareBB[s] = (1ULL << s); From bad18bccb60c874410edd3f61624696d3abc3cbc Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Tue, 12 Mar 2019 08:35:10 +0100 Subject: [PATCH 15/17] Increase thread stack for OS X (#2035) On OS X threads other than the main thread are created with a reduced stack size of 512KB by default, this is dangerously low for deep searches, so adjust it to TH_STACK_SIZE. The implementation calls pthread_create() with proper stack size parameter. Verified for no regression at STC enabling the patch on all platforms where pthread is supported. LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 50873 W: 9768 L: 9700 D: 31405 No functional change. --- src/syzygy/tbprobe.cpp | 2 +- src/thread.h | 4 +- src/{thread_win32.h => thread_win32_osx.h} | 48 ++++++++++++++++++++-- 3 files changed, 48 insertions(+), 6 deletions(-) rename src/{thread_win32.h => thread_win32_osx.h} (66%) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 01bbc7ef..7864486c 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -32,7 +32,7 @@ #include "../movegen.h" #include "../position.h" #include "../search.h" -#include "../thread_win32.h" +#include "../thread_win32_osx.h" #include "../types.h" #include "../uci.h" diff --git a/src/thread.h b/src/thread.h index 686441cb..af506609 100644 --- a/src/thread.h +++ b/src/thread.h @@ -32,7 +32,7 @@ #include "pawns.h" #include "position.h" #include "search.h" -#include "thread_win32.h" +#include "thread_win32_osx.h" /// Thread class keeps together all the thread-related stuff. We use @@ -46,7 +46,7 @@ class Thread { ConditionVariable cv; size_t idx; bool exit = false, searching = true; // Set before starting std::thread - std::thread stdThread; + NativeThread stdThread; public: explicit Thread(size_t); diff --git a/src/thread_win32.h b/src/thread_win32_osx.h similarity index 66% rename from src/thread_win32.h rename to src/thread_win32_osx.h index 5c914df3..88900540 100644 --- a/src/thread_win32.h +++ b/src/thread_win32_osx.h @@ -18,8 +18,8 @@ along with this program. If not, see . */ -#ifndef THREAD_WIN32_H_INCLUDED -#define THREAD_WIN32_H_INCLUDED +#ifndef THREAD_WIN32_OSX_H_INCLUDED +#define THREAD_WIN32_OSX_H_INCLUDED /// STL thread library used by mingw and gcc when cross compiling for Windows /// relies on libwinpthread. Currently libwinpthread implements mutexes directly @@ -33,6 +33,7 @@ #include #include +#include #if defined(_WIN32) && !defined(_MSC_VER) @@ -67,4 +68,45 @@ typedef std::condition_variable ConditionVariable; #endif -#endif // #ifndef THREAD_WIN32_H_INCLUDED +/// On OSX threads other than the main thread are created with a reduced stack +/// size of 512KB by default, this is dangerously low for deep searches, so +/// adjust it to TH_STACK_SIZE. The implementation calls pthread_create() with +/// proper stack size parameter. + +#if defined(__APPLE__) + +#include + +static const size_t TH_STACK_SIZE = 2 * 1024 * 1024; + +template > +void* start_routine(void* ptr) +{ + P* p = reinterpret_cast(ptr); + (p->first->*(p->second))(); // Call member function pointer + delete p; + return NULL; +} + +class NativeThread { + + pthread_t thread; + +public: + template> + explicit NativeThread(void(T::*fun)(), T* obj) { + pthread_attr_t attr_storage, *attr = &attr_storage; + pthread_attr_init(attr); + pthread_attr_setstacksize(attr, TH_STACK_SIZE); + pthread_create(&thread, attr, start_routine, new P(obj, fun)); + } + void join() { pthread_join(thread, NULL); } +}; + +#else // Default case: use STL classes + +typedef std::thread NativeThread; + +#endif + +#endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED From 66818f2e85732644708e23b3f2c2e544abfbc3b0 Mon Sep 17 00:00:00 2001 From: CoffeeOne Date: Wed, 20 Mar 2019 14:50:41 +0100 Subject: [PATCH 16/17] Skip skipping thread scheme (#1972) Several simplification tests (all with the bounds [-3,1]) were run: 5+0.05 8 threads, failed very quickly: http://tests.stockfishchess.org/tests/view/5c439a020ebc5902bb5d3970 20+0.2 8 threads, also failed, but needed a lot more games: http://tests.stockfishchess.org/tests/view/5c44b1b70ebc5902bb5d4e34 60+0.6 8 threads passed: http://tests.stockfishchess.org/tests/view/5c48bfe40ebc5902bca15325 60+0.6 4 threads passed: http://tests.stockfishchess.org/tests/view/5c4b71a00ebc593af5d49904 No functional change. --- src/search.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ba1fef4d..cf934fc9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -61,10 +61,6 @@ namespace { // Different node types, used as a template parameter enum NodeType { NonPV, PV }; - // Sizes and phases of the skip-blocks, used for distributing search depths across the threads - constexpr int SkipSize[] = { 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4 }; - constexpr int SkipPhase[] = { 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7 }; - // Razor and futility margins constexpr int RazorMargin = 600; Value futility_margin(Depth d, bool improving) { @@ -338,14 +334,6 @@ void Thread::search() { && !Threads.stop && !(Limits.depth && mainThread && rootDepth / ONE_PLY > Limits.depth)) { - // Distribute search depths across the helper threads - if (idx > 0) - { - int i = (idx - 1) % 20; - if (((rootDepth / ONE_PLY + SkipPhase[i]) / SkipSize[i]) % 2) - continue; // Retry with an incremented rootDepth - } - // Age out PV variability metric if (mainThread) mainThread->bestMoveChanges *= 0.517; From d320de761951687fa55716da1bd786df4c2cb14b Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sun, 10 Mar 2019 21:57:48 +0000 Subject: [PATCH 17/17] Remove !extension check #2045 While looking at pruning using see_ge() (which is very valuable) it became apparent that the !extension test is not adding any value - simplify it away. STC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 56843 W: 12621 L: 12569 D: 31653 http://tests.stockfishchess.org/tests/view/5c8588cb0ebc5925cffe77f4 LTC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 78622 W: 13223 L: 13195 D: 52204 http://tests.stockfishchess.org/tests/view/5c8611cc0ebc5925cffe7f86 Further work could be to optimize the remaining see_ge() test. The idea of less pruning at higher depths is valuable, but perhaps the test (-PawnValueEg * depth) can be improved. Bench: 3188688 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cf934fc9..7043e49d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -970,8 +970,7 @@ moves_loop: // When in check, search starts from here if (!pos.see_ge(move, Value(-29 * lmrDepth * lmrDepth))) continue; } - else if ( !extension // (~20 Elo) - && !pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) + else if (!pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) // (~20 Elo) continue; }