From 82d66f6b72c656a3efc4fd54b6eb489a8f74a945 Mon Sep 17 00:00:00 2001 From: 31m059 <37052095+31m059@users.noreply.github.com> Date: Thu, 11 Jul 2019 09:14:57 -0400 Subject: [PATCH 01/72] Exclude passed pawns from Attacked2Unsupported We recently added a bonus for double pawn attacks on unsupported enemy pawns, on June 27. However, it is possible that the unsupported pawn may become a passer by simply pushing forward out of the double attack. By rewarding double attacks, we may inadvertently reward the creation of enemy passers, by encouraging both of our would-be stoppers to attack the enemy pawn even if there is no opposing friendly pawn on the same file. Here, we revise this term to exclude passed pawns. In order to simplify the code with this change included, we non-functionally rewrite Attacked2Unsupported to be a penalty for enemy attacks on friendly pawns, rather than a bonus for our attacks on enemy pawns. This allows us to exclude passed pawns with a simple & ~e->passedPawns[Us], while passedPawns[Them] is not yet defined in this part of the code. This dramatically reduces the proportion of positions in which Attacked2Unsupported is applied, to about a third of the original. To compensate, maintaining the same average effect across our bench positions, we nearly triple Attacked2Unsupported from S(0, 20) to S(0, 56). Although this pawn formation is rare, it is worth more than half a pawn in the endgame! STC: (stopped automatically by fishtest after 250,000 games) LLR: -0.87 (-2.94,2.94) [0.50,4.50] Total: 250000 W: 56585 L: 55383 D: 138032 http://tests.stockfishchess.org/tests/view/5d25795e0ebc5925cf0cfb51 LTC: LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 81038 W: 13965 L: 13558 D: 53515 http://tests.stockfishchess.org/tests/view/5d25f3920ebc5925cf0d10dd Closes https://github.com/official-stockfish/Stockfish/pull/2233 Bench: 3765158 --- src/evaluate.cpp | 2 +- src/pawns.cpp | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c4e02e4e..a52cdf09 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -655,7 +655,7 @@ namespace { !(unsafeSquares & blockSq) ? 9 : 0 ; - // Assign a larger bonus if the block square is defended. + // Assign a larger bonus if the block square is defended if (defendedSquares & blockSq) k += 5; diff --git a/src/pawns.cpp b/src/pawns.cpp index 45c40471..0d3a57bf 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -35,8 +35,8 @@ 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); - constexpr Score Attacked2Unsupported = S( 0, 20); + constexpr Score WeakUnopposed = S(13, 27); + constexpr Score Attacked2Unsupported = S(0, 56); // Connected pawn bonus constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; @@ -84,11 +84,6 @@ namespace { 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) { @@ -116,8 +111,8 @@ namespace { // Passed pawns will be properly scored in evaluation because we need // 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. + // which could become passed after one or two pawn pushes when they + // are not attacked more times than defended. if ( !(stoppers ^ lever) || (!(stoppers ^ leverPush) && popcount(phalanx) >= popcount(leverPush))) e->passedPawns[Us] |= s; @@ -149,6 +144,12 @@ namespace { score -= Doubled; } + // Unsupported friendly pawns attacked twice by the enemy + score -= Attacked2Unsupported * popcount( ourPawns + & pawn_double_attacks_bb(theirPawns) + & ~pawn_attacks_bb(ourPawns) + & ~e->passedPawns[Us]); + return score; } From 389e60741f308bb23904baec0d52552935162e0f Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Fri, 12 Jul 2019 07:22:46 +0200 Subject: [PATCH 02/72] Late Move reduction and continuation history Update continuation history after LMR-triggered full depth research. Directly after a LMR-triggered full depth research, we update the continuation history for quiet moves (but with only half stat bonus). STC: LLR: 2.96 (-2.94,2.94) [0.50,4.50] Total: 39657 W: 8966 L: 8604 D: 22087 http://tests.stockfishchess.org/tests/view/5d279fa40ebc5925cf0d4566 LTC: LLR: 2.96 (-2.94,2.94) [0.50,3.50] Total: 32582 W: 5740 L: 5427 D: 21415 http://tests.stockfishchess.org/tests/view/5d27dbf90ebc5925cf0d4b7e Bench: 3239357 --- src/search.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index e6446768..ea0c64f1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -578,7 +578,7 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue; - bool ttHit, ttPv, inCheck, givesCheck, improving; + bool ttHit, ttPv, inCheck, givesCheck, improving, doLMR; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount, singularLMR; @@ -1122,15 +1122,26 @@ moves_loop: // When in check, search starts from here value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); - doFullDepthSearch = (value > alpha && d != newDepth); + doFullDepthSearch = (value > alpha && d != newDepth), doLMR = true; } else - doFullDepthSearch = !PvNode || moveCount > 1; + doFullDepthSearch = !PvNode || moveCount > 1, doLMR = false; // Step 17. Full depth search when LMR is skipped or fails high if (doFullDepthSearch) + { value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); + if (doLMR && !captureOrPromotion) + { + int bonus = stat_bonus(newDepth) / 2; + if (value <= alpha) + bonus = -bonus; + + update_continuation_histories(ss, movedPiece, to_sq(move), bonus); + } + } + // For PV nodes only, do a full PV search on the first move or after a fail // high (in the latter case search only if value < beta), otherwise let the // parent node fail low with value <= alpha and try another move. From ff69d570d774c465b63ed65f8f14afdaac4eb107 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Fri, 12 Jul 2019 10:17:24 +0200 Subject: [PATCH 03/72] Full bonus for LMR stats update Simplify previous commit by using the full bonus for LMR-triggered stats update. STC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 23684 W: 5255 L: 5137 D: 13292 http://tests.stockfishchess.org/tests/view/5d2826660ebc5925cf0d5180 LTC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 16245 W: 2832 L: 2704 D: 10709 http://tests.stockfishchess.org/tests/view/5d282e9c0ebc5925cf0d529b Closes https://github.com/official-stockfish/Stockfish/pull/2236 Bench: 3361902 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ea0c64f1..bd5ae75e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1134,9 +1134,8 @@ moves_loop: // When in check, search starts from here if (doLMR && !captureOrPromotion) { - int bonus = stat_bonus(newDepth) / 2; - if (value <= alpha) - bonus = -bonus; + int bonus = value > alpha ? stat_bonus(newDepth) + : -stat_bonus(newDepth); update_continuation_histories(ss, movedPiece, to_sq(move), bonus); } From a0360cc2d4397edaad590cdf131fef95915b55c0 Mon Sep 17 00:00:00 2001 From: 31m059 <37052095+31m059@users.noreply.github.com> Date: Sun, 14 Jul 2019 08:40:45 -0400 Subject: [PATCH 04/72] Linear formula for w. Bench: 3328507 (#2239) In Stockfish, both the middlegame and endgame bonus for a passed pawn are calculated as a product of two factors. The first is k, chosen based on the presence of defended and unsafe squares. The second is w, a quadratic function of the pawn's rank. Both are only applied if the pawn's relative rank is at least RANK_4. It does not appear that the complexity of a quadratic function is necessary for w. Here, we replace it with a simpler linear one, which performs equally at both STC and LTC. STC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 46814 W: 10386 L: 10314 D: 26114 http://tests.stockfishchess.org/tests/view/5d29686e0ebc5925cf0d76a1 LTC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 82372 W: 13845 L: 13823 D: 54704 http://tests.stockfishchess.org/tests/view/5d2980650ebc5925cf0d7bfd Bench: 3328507 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a52cdf09..b20c93b7 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -622,7 +622,7 @@ namespace { if (r > RANK_3) { - int w = (r-2) * (r-2) + 2; + int w = 5 * r - 13; Square blockSq = s + Up; // Adjust bonus based on the king's proximity From 0a8a3b8d9c1936c75a71d899d4bbfd6839621318 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 14 Jul 2019 15:41:28 +0300 Subject: [PATCH 05/72] tviigg. (#2238) Current master code made sence when we had 2 types of bonuses for protected path to queen. But it was simplified so we have only one bonus now and code was never cleaned. This non-functional simplification removes useless defendedsquares bitboard and removes one bitboard assignment (defendedSquares &= attackedBy[Us][ALL_PIECES] + defendedSquares & blockSq becomes just attackedBy[Us][ALL_PIECES] & blockSq also we never assign defendedSquares = squaresToQueen because we don't need it). So should be small non-functional speedup. Passed simplification SPRT. http://tests.stockfishchess.org/tests/view/5d2966ef0ebc5925cf0d7659 LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 23319 W: 5152 L: 5034 D: 13133 bench 3361902 --- src/evaluate.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index b20c93b7..8b017a5c 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -605,7 +605,7 @@ namespace { return std::min(distance(pos.square(c), s), 5); }; - Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares; + Bitboard b, bb, squaresToQueen, unsafeSquares; Score score = SCORE_ZERO; b = pe->passed_pawns(Us); @@ -636,14 +636,11 @@ namespace { // If the pawn is free to advance, then increase the bonus if (pos.empty(blockSq)) { - defendedSquares = squaresToQueen = forward_file_bb(Us, s); + squaresToQueen = forward_file_bb(Us, s); unsafeSquares = passed_pawn_span(Us, s); bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN); - if (!(pos.pieces(Us) & bb)) - defendedSquares &= attackedBy[Us][ALL_PIECES]; - if (!(pos.pieces(Them) & bb)) unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them); @@ -656,7 +653,7 @@ namespace { 0 ; // Assign a larger bonus if the block square is defended - if (defendedSquares & blockSq) + if ((pos.pieces(Us) & bb) || (attackedBy[Us][ALL_PIECES] & blockSq)) k += 5; bonus += make_score(k * w, k * w); From 13ba67801f0331e3ffde23794b989765af5a9aa2 Mon Sep 17 00:00:00 2001 From: 31m059 <37052095+31m059@users.noreply.github.com> Date: Sun, 14 Jul 2019 08:42:30 -0400 Subject: [PATCH 06/72] Just blockSq, not forward file. Bench: 3377831 (#2240) This is another functional simplification to Stockfish passed pawn evaluation. Stockfish evaluates some pawns which are not yet passed as "candidate" passed pawns, which are given half the bonus of fully passed ones. Prior to this commit, Stockfish considered a passed pawn to be a "candidate" if (a) it would not be a passed pawn if moved one square forward (the blocking square), or (b) there were other pawns (of either color) in front of it on the file. This latter condition used a fairly complicated method, forward_file_bb; here, rather than inspect the entire forward file, we simply re-use the blocking square. As a result, some pawns previously considered "candidates", but which are able to push forward, no longer have their bonus halved. Simplification tests passed quickly at both STC and LTC. The results from both tests imply that this simplification is, most likely, additionally a small Elo gain, with a LTC likelihood of superiority of 87 percent. STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 12908 W: 2909 L: 2770 D: 7229 http://tests.stockfishchess.org/tests/view/5d2a1c880ebc5925cf0d9006 LTC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 20723 W: 3591 L: 3470 D: 13662 http://tests.stockfishchess.org/tests/view/5d2a21fd0ebc5925cf0d9118 Bench: 3377831 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 8b017a5c..9a67a8e4 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -663,7 +663,7 @@ namespace { // Scale down bonus for candidate passers which need more than one // pawn push to become passed, or have a pawn in front of them. if ( !pos.pawn_passed(Us, s + Up) - || (pos.pieces(PAWN) & forward_file_bb(Us, s))) + || (pos.pieces(PAWN) & (s + Up))) bonus = bonus / 2; score += bonus + PassedFile[file_of(s)]; From 650aeaf2420bdac00de03963132d82a415193a1c Mon Sep 17 00:00:00 2001 From: protonspring Date: Sun, 14 Jul 2019 06:46:10 -0600 Subject: [PATCH 07/72] Remove std::pow from reduction. (#2234) This is a functional simplification that removes the std::pow from reduction. The resulting reduction values are within 1% of master. This is a simplification because i believe an fp addition and multiplication is much faster than a call to std::pow() which is historically slow and performance varies widely on different architectures. STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 23471 W: 5245 L: 5127 D: 13099 http://tests.stockfishchess.org/tests/view/5d27ac1b0ebc5925cf0d476b LTC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 51533 W: 8736 L: 8665 D: 34132 http://tests.stockfishchess.org/tests/view/5d27b74e0ebc5925cf0d493c Bench 3765158 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index bd5ae75e..df19108f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -503,7 +503,7 @@ void Thread::search() { // 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; + double reduction = (1.25 + mainThread->previousTimeReduction) / (2.25 * timeReduction); // Use part of the gained time from a previous stable move for the current move for (Thread* th : Threads) From 0dbc72d82e7f5f314499b22a04dcef45edcdba9a Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 14 Jul 2019 14:47:50 +0200 Subject: [PATCH 08/72] UCI_Elo implementation (#2225) This exploits the recent fractional Skill Level, and is a result from some discussion in #2221 and the older #758. Basically, if UCI_LimitStrength is set, it will internally convert UCI_Elo to a matching fractional Skill Level. The Elo estimate is based on games at TC 60+0.6, Hash 64Mb, 8moves_v3.pgn, rated with Ordo, anchored to goldfish1.13 (CCRL 40/4 ~2000). Note that this is mostly about internal consistency, the anchoring to CCRL is a bit weak, e.g. within this tournament, goldfish and sungorus only have a 200Elo difference, their rating difference on CCRL is 300Elo. I propose that we continue to expose 'Skill Level' as an UCI option, for backwards compatibility. The result of a tournament under those conditions are given by the following table, where the player name reflects the UCI_Elo. # PLAYER : RATING ERROR POINTS PLAYED (%) CFS(%) 1 Elo2837 : 2792.2 50.8 536.5 711 75 100 2 Elo2745 : 2739.0 49.0 487.5 711 69 100 3 Elo2654 : 2666.4 49.2 418.0 711 59 100 4 Elo2562 : 2604.5 38.5 894.5 1383 65 100 5 Elo2471 : 2515.2 38.1 651.5 924 71 100 6 Elo2380 : 2365.9 35.4 478.5 924 52 100 7 Elo2289 : 2290.0 28.0 864.0 1596 54 100 8 sungorus1.4 : 2204.9 27.8 680.5 1596 43 60 9 Elo2197 : 2201.1 30.1 523.5 924 57 100 10 Elo2106 : 2103.8 24.5 730.5 1428 51 100 11 Elo2014 : 2030.5 30.3 377.5 756 50 98 12 goldfish1.13 : 2000.0 ---- 511.0 1428 36 100 13 Elo1923 : 1928.5 30.9 641.5 1260 51 100 14 Elo1831 : 1829.0 42.1 370.5 756 49 100 15 Elo1740 : 1738.3 42.9 277.5 756 37 100 16 Elo1649 : 1625.0 42.1 525.5 1260 42 100 17 Elo1558 : 1521.5 49.9 298.0 756 39 100 18 Elo1467 : 1471.3 51.3 246.5 756 33 100 19 Elo1375 : 1407.1 51.9 183.0 756 24 --- It can be observed that all set Elos correspond within the error bars with the observed Ordo rating. No functional change --- Readme.md | 11 ++++++++++- src/search.cpp | 13 ++++++++++--- src/ucioption.cpp | 2 ++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index be324763..10ffdeae 100644 --- a/Readme.md +++ b/Readme.md @@ -55,7 +55,16 @@ Currently, Stockfish has the following UCI options: Leave at 1 for best performance. * #### Skill Level - Lower the Skill Level in order to make Stockfish play weaker. + Lower the Skill Level in order to make Stockfish play weaker (see also UCI_LimitStrength). + Internally, MultiPV is enabled, and with a certain probability depending on the Skill Level a + weaker move will be played. + + * #### UCI_LimitStrength + Enable weaker play aiming for an Elo rating as set by UCI_Elo. This option overrides Skill Level. + + * #### UCI_Elo + If enabled by UCI_LimitStrength, aim for an engine strength of the given Elo. + This Elo rating has been calibrated at a time control of 60s+0.6s and anchored to CCRL 40/4. * #### Move Overhead Assume a time delay of x ms due to network and GUI overheads. This is useful to diff --git a/src/search.cpp b/src/search.cpp index df19108f..2c2321ee 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -271,7 +271,7 @@ void MainThread::search() { // Check if there are threads with a better score than main thread if ( Options["MultiPV"] == 1 && !Limits.depth - && !Skill(Options["Skill Level"]).enabled() + && !(Skill(Options["Skill Level"]).enabled() || Options["UCI_LimitStrength"]) && rootMoves[0].pv[0] != MOVE_NONE) { std::map votes; @@ -335,11 +335,18 @@ void Thread::search() { beta = VALUE_INFINITE; multiPV = Options["MultiPV"]; + // Pick integer skill levels, but non-deterministically round up or down // such that the average integer skill corresponds to the input floating point one. + // UCI_Elo is converted to a suitable fractional skill level, using anchoring + // to CCRL Elo (goldfish 1.13 = 2000) and a fit through Ordo derived Elo + // for match (TC 60+0.6) results spanning a wide range of k values. PRNG rng(now()); - int intLevel = int(Options["Skill Level"]) + - ((Options["Skill Level"] - int(Options["Skill Level"])) * 1024 > rng.rand() % 1024 ? 1 : 0); + double floatLevel = Options["UCI_LimitStrength"] ? + clamp(std::pow((Options["UCI_Elo"] - 1346.6) / 143.4, 1 / 0.806), 0.0, 20.0) : + double(Options["Skill Level"]); + int intLevel = int(floatLevel) + + ((floatLevel - int(floatLevel)) * 1024 > rng.rand() % 1024 ? 1 : 0); Skill skill(intLevel); // When playing with strength handicap enable MultiPV search that we will diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 813a0890..23c0c480 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -74,6 +74,8 @@ void init(OptionsMap& o) { o["nodestime"] << Option(0, 0, 10000); o["UCI_Chess960"] << Option(false); o["UCI_AnalyseMode"] << Option(false); + o["UCI_LimitStrength"] << Option(false); + o["UCI_Elo"] << Option(1350, 1350, 2850); o["SyzygyPath"] << Option("", on_tb_path); o["SyzygyProbeDepth"] << Option(1, 1, 100); o["Syzygy50MoveRule"] << Option(true); From 3ec362e4b299128b91d961250b444d930b4c60fa Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Sun, 14 Jul 2019 13:13:06 -0400 Subject: [PATCH 10/72] Space Invaders Try a more ambitius simplification of the space bonus STC http://tests.stockfishchess.org/tests/view/5d2b62c90ebc5925cf0da2a4 LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 51299 W: 11320 L: 11257 D: 28722 LTC http://tests.stockfishchess.org/tests/view/5d2bac270ebc5925cf0db215 LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 49761 W: 8409 L: 8335 D: 33017 Closes https://github.com/official-stockfish/Stockfish/pull/2243 bench: 3395999 --- src/evaluate.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 9a67a8e4..10dddc7d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -133,7 +133,6 @@ 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); @@ -705,12 +704,10 @@ namespace { behind |= shift(behind); behind |= shift(behind); - int bonus = popcount(safe) + popcount(behind & safe); + int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]); 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 19509e5f1313e3693a7ee56531326eed3c62a1af Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Tue, 16 Jul 2019 14:56:52 +0300 Subject: [PATCH 11/72] Tweak LMR and killers Give extra stat bonus/malus in case of LMR for killers. passed STC http://tests.stockfishchess.org/tests/view/5d2c8e760ebc5925cf0dcf23 LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 67188 W: 15030 L: 14534 D: 37624 passed LTC http://tests.stockfishchess.org/tests/view/5d2d0ce40ebc5925cf0de115 LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 144355 W: 24739 L: 24153 D: 95463 Closes https://github.com/official-stockfish/Stockfish/pull/2246 bench 3723147 --- src/search.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 2c2321ee..1fc0bf1e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1144,6 +1144,9 @@ moves_loop: // When in check, search starts from here int bonus = value > alpha ? stat_bonus(newDepth) : -stat_bonus(newDepth); + if (move == ss->killers[0]) + bonus += bonus / 4; + update_continuation_histories(ss, movedPiece, to_sq(move), bonus); } } From fd96cba67603c9c2462d41887f79d4b6ac8920b1 Mon Sep 17 00:00:00 2001 From: Lolligerhans Date: Tue, 16 Jul 2019 10:14:09 +0200 Subject: [PATCH 12/72] No influence on unsafeSquares of passers by pieces Remove their pieces from influencing 'unsafeSquares' in passer evaluation. STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 36421 W: 8170 L: 8078 D: 20173 http://tests.stockfishchess.org/tests/view/5d22fc8e0ebc5925cf0cb26e LTC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 18927 W: 3253 L: 3129 D: 12545 http://tests.stockfishchess.org/tests/view/5d26e2b20ebc5925cf0d3218 Closes https://github.com/official-stockfish/Stockfish/pull/2248 Bench: 3285659 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 10dddc7d..23af60f3 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -641,7 +641,7 @@ namespace { bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN); if (!(pos.pieces(Them) & bb)) - unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them); + unsafeSquares &= attackedBy[Them][ALL_PIECES]; // 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 From 9dc57b660eb22190166c006eb99319c2b321e4f7 Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Sat, 20 Jul 2019 11:38:45 -0400 Subject: [PATCH 13/72] Passed file cleanup Protonspring had a successful functional simplification that removes the PassedFile array using a simple linear equation. Merge the additive term S(5, 10) of protonspring passed file simplification (pull request https://github.com/official-stockfish/Stockfish/pull/2250) into the PassedRank array. This harmless change has a different bench because the candidate passer evaluation will always get less compared to #2250, as we apply bonus = bonus /2. Tested as a non-regression against #2250 Passed STC http://tests.stockfishchess.org/tests/view/5d33427e0ebc5925cf0e6fa2 LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 81459 W: 18174 L: 18171 D: 45114 Passed LTC http://tests.stockfishchess.org/tests/view/5d335c8d0ebc5925cf0e731e LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 18525 W: 3176 L: 3052 D: 12297 Closes https://github.com/official-stockfish/Stockfish/pull/2250 Closes https://github.com/official-stockfish/Stockfish/pull/2251 Bench: 3859856 --- src/evaluate.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 23af60f3..7359eb92 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -123,13 +123,7 @@ namespace { // PassedRank[Rank] contains a bonus according to the rank of a passed pawn constexpr Score PassedRank[RANK_NB] = { - S(0, 0), S(5, 18), S(12, 23), S(10, 31), S(57, 62), S(163, 167), S(271, 250) - }; - - // PassedFile[File] contains a bonus according to the file of a passed pawn - constexpr Score PassedFile[FILE_NB] = { - S( -1, 7), S( 0, 9), S(-9, -8), S(-30,-14), - S(-30,-14), S(-9, -8), S( 0, 9), S( -1, 7) + S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260) }; // Assorted bonuses and penalties @@ -142,6 +136,7 @@ namespace { constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score MinorBehindPawn = S( 18, 3); constexpr Score Outpost = S( 18, 6); + constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RookOnPawn = S( 10, 32); @@ -616,6 +611,7 @@ namespace { assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up))); int r = relative_rank(Us, s); + File f = file_of(s); Score bonus = PassedRank[r]; @@ -665,7 +661,7 @@ namespace { || (pos.pieces(PAWN) & (s + Up))) bonus = bonus / 2; - score += bonus + PassedFile[file_of(s)]; + score += bonus - PassedFile * std::min(f, ~f); } if (T) From dc243a3c880d0a736fb93848cf56e3221e07f8a3 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Sun, 21 Jul 2019 11:25:58 -0400 Subject: [PATCH 14/72] LMR Tweak Reset statScore to zero if negative and most stats shows >= 0 STC: LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 23097 W: 5242 L: 4963 D: 12892 http://tests.stockfishchess.org/tests/view/5d31dd650ebc5925cf0e598f LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 227597 W: 39013 L: 38191 D: 150393 http://tests.stockfishchess.org/tests/view/5d31fcdf0ebc5925cf0e5c13 Closes https://github.com/official-stockfish/Stockfish/pull/2252 Bench: 3242229 --- src/search.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 1fc0bf1e..f50fdf88 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1114,6 +1114,13 @@ moves_loop: // When in check, search starts from here + (*contHist[3])[movedPiece][to_sq(move)] - 4000; + // Reset statScore to zero if negative and most stats shows >= 0 + if ( ss->statScore < 0 + && (*contHist[0])[movedPiece][to_sq(move)] >= 0 + && (*contHist[1])[movedPiece][to_sq(move)] >= 0 + && thisThread->mainHistory[us][from_to(move)] >= 0) + ss->statScore = 0; + // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) if (ss->statScore >= 0 && (ss-1)->statScore < 0) r -= ONE_PLY; From 33c3a0465358116560174ae7421144be714e43f9 Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Thu, 25 Jul 2019 09:02:26 +0200 Subject: [PATCH 15/72] Pawn clean up Non functional simplification when we find the passed pawns in pawn.cpp and some code clean up. It also better follows the pattern "flag the pawn" and "score the pawn". ------------------------- The idea behind the third condition for candidate passed pawn is a little bit difficult to visualize. Just for the record, the idea is the following: Consider White e5 d4 against black e6. d4 can (in some endgames) push to d5 and lever e6. Thanks to this sacrifice, or after d5xe6, we consider e5 as "passed". However: - if White e5/d4 against black e6/c6: d4 cannot safely push to d5 since d5 is double attacked; - if White e5/d4 against black e6/d5: d4 cannot safely push to d5 since it is occupied. This is exactly what the following expression does: ``` && (shift(support) & ~(theirPawns | dblAttackThem))) ``` -------------------------- http://tests.stockfishchess.org/tests/view/5d3325bb0ebc5925cf0e6e91 LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 124666 W: 27586 L: 27669 D: 69411 Closes https://github.com/official-stockfish/Stockfish/pull/2255 No functional change --- src/pawns.cpp | 43 ++++++++++++++++++++++--------------------- src/search.cpp | 2 +- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 0d3a57bf..9755c2ec 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -35,8 +35,8 @@ namespace { constexpr Score Backward = S( 9, 24); constexpr Score Doubled = S(11, 56); constexpr Score Isolated = S( 5, 15); + constexpr Score WeakLever = S( 0, 56); constexpr Score WeakUnopposed = S(13, 27); - constexpr Score Attacked2Unsupported = S(0, 56); // Connected pawn bonus constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; @@ -73,13 +73,15 @@ namespace { Bitboard b, neighbours, stoppers, doubled, support, phalanx; Bitboard lever, leverPush; Square s; - bool opposed, backward; + bool opposed, backward, passed; Score score = SCORE_ZERO; const Square* pl = pos.squares(Us); Bitboard ourPawns = pos.pieces( Us, PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN); + Bitboard doubleAttackThem = pawn_double_attacks_bb(theirPawns); + e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0; e->kingSquares[Us] = SQ_NONE; e->pawnAttacks[Us] = pawn_attacks_bb(ourPawns); @@ -109,21 +111,20 @@ namespace { backward = !(neighbours & forward_ranks_bb(Them, s)) && (stoppers & (leverPush | (s + Up))); - // Passed pawns will be properly scored in evaluation because we need - // full attack info to evaluate them. Include also not passed pawns - // which could become passed after one or two pawn pushes when they - // are not attacked more times than defended. - if ( !(stoppers ^ lever) || - (!(stoppers ^ leverPush) && popcount(phalanx) >= popcount(leverPush))) - e->passedPawns[Us] |= s; + // A pawn is passed if one of the three following conditions is true: + // (a) there is no stoppers except some levers + // (b) the only stoppers are the leverPush, but we outnumber them + // (c) there is only one front stopper which can be levered. + passed = !(stoppers ^ lever) + || ( !(stoppers ^ leverPush) + && popcount(phalanx) >= popcount(leverPush)) + || ( stoppers == square_bb(s + Up) && r >= RANK_5 + && (shift(support) & ~(theirPawns | doubleAttackThem))); - else if (stoppers == square_bb(s + Up) && r >= RANK_5) - { - b = shift(support) & ~theirPawns; - while (b) - if (!more_than_one(theirPawns & PawnAttacks[Us][pop_lsb(&b)])) - e->passedPawns[Us] |= s; - } + // Passed pawns will be properly scored later in evaluation when we have + // full attack info. + if (passed) + e->passedPawns[Us] |= s; // Score this pawn if (support | phalanx) @@ -144,11 +145,11 @@ namespace { score -= Doubled; } - // Unsupported friendly pawns attacked twice by the enemy - score -= Attacked2Unsupported * popcount( ourPawns - & pawn_double_attacks_bb(theirPawns) - & ~pawn_attacks_bb(ourPawns) - & ~e->passedPawns[Us]); + // Penalize the unsupported and non passed pawns attacked twice by the enemy + b = ourPawns + & doubleAttackThem + & ~(e->pawnAttacks[Us] | e->passedPawns[Us]); + score -= WeakLever * popcount(b); return score; } diff --git a/src/search.cpp b/src/search.cpp index f50fdf88..222be393 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1152,7 +1152,7 @@ moves_loop: // When in check, search starts from here : -stat_bonus(newDepth); if (move == ss->killers[0]) - bonus += bonus / 4; + bonus += bonus / 4; update_continuation_histories(ss, movedPiece, to_sq(move), bonus); } From acdda38b93361f10e331a6d951d6870577efdace Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 24 Jul 2019 17:30:59 +0300 Subject: [PATCH 16/72] Tweak of SEE pruning condition passed STC http://tests.stockfishchess.org/tests/view/5d386bda0ebc5925cf0ef49a LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 56874 W: 12820 L: 12373 D: 31681 passed LTC http://tests.stockfishchess.org/tests/view/5d38873a0ebc5925cf0ef86e LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 43512 W: 7547 L: 7247 D: 28718 Additional thanks to @locutus2 , @miguel-l and @xoto10 for fruitful discussion. There may be some more elo there since this tweak was the first one and numbers are more or less arbitrary. Closes https://github.com/official-stockfish/Stockfish/pull/2256 Bench 3935523 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 222be393..09df1ac2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1041,7 +1041,7 @@ moves_loop: // When in check, search starts from here continue; // Prune moves with negative SEE (~10 Elo) - if (!pos.see_ge(move, Value(-29 * lmrDepth * lmrDepth))) + if (!pos.see_ge(move, Value(-(31 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) continue; } else if ( (!givesCheck || !extension) From aec918a2b6ee931826ef19db1726950976da7ffe Mon Sep 17 00:00:00 2001 From: protonspring Date: Tue, 16 Jul 2019 06:08:58 -0600 Subject: [PATCH 17/72] Remove operators for color This is a non-functional and untested simplification. The increment operator for color isn't really necessary and seems a bit unnatural to me. Passed STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 47027 W: 10589 L: 10518 D: 25920 http://tests.stockfishchess.org/tests/view/5d3472d10ebc5925cf0e8d3e Closes https://github.com/official-stockfish/Stockfish/pull/2247 No functional change --- src/bitboard.cpp | 2 +- src/material.cpp | 4 ++-- src/position.cpp | 4 ++-- src/syzygy/tbprobe.cpp | 2 +- src/types.h | 1 - 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 281579c4..2afd3766 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -80,7 +80,7 @@ void Bitboards::init() { int steps[][5] = { {}, { 7, 9 }, { 6, 10, 15, 17 }, {}, {}, {}, { 1, 7, 8, 9 } }; - for (Color c = WHITE; c <= BLACK; ++c) + for (Color c : { WHITE, BLACK }) for (PieceType pt : { PAWN, KNIGHT, KING }) for (Square s = SQ_A1; s <= SQ_H8; ++s) for (int i = 0; steps[pt][i]; ++i) diff --git a/src/material.cpp b/src/material.cpp index 3a05f3fa..11d4c687 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -140,7 +140,7 @@ Entry* probe(const Position& pos) { if ((e->evaluationFunction = Endgames::probe(key)) != nullptr) return e; - for (Color c = WHITE; c <= BLACK; ++c) + for (Color c : { WHITE, BLACK }) if (is_KXK(pos, c)) { e->evaluationFunction = &EvaluateKXK[c]; @@ -160,7 +160,7 @@ Entry* probe(const Position& pos) { // We didn't find any specialized scaling function, so fall back on generic // ones that refer to more than one material distribution. Note that in this // case we don't return after setting the function. - for (Color c = WHITE; c <= BLACK; ++c) + for (Color c : { WHITE, BLACK }) { if (is_KBPsK(pos, c)) e->scalingFunction[c] = &ScaleKBPsK[c]; diff --git a/src/position.cpp b/src/position.cpp index 9f06e174..fbde810b 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1299,8 +1299,8 @@ bool Position::pos_is_ok() const { assert(0 && "pos_is_ok: Index"); } - for (Color c = WHITE; c <= BLACK; ++c) - for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1)) + for (Color c : { WHITE, BLACK }) + for (CastlingSide s : {KING_SIDE, QUEEN_SIDE}) { if (!can_castle(c | s)) continue; diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 7864486c..90c86388 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -367,7 +367,7 @@ TBTable::TBTable(const std::string& code) : TBTable() { hasPawns = pos.pieces(PAWN); hasUniquePieces = false; - for (Color c = WHITE; c <= BLACK; ++c) + for (Color c : {WHITE, BLACK}) for (PieceType pt = PAWN; pt < KING; ++pt) if (popcount(pos.pieces(c, pt)) == 1) hasUniquePieces = true; diff --git a/src/types.h b/src/types.h index b9c01fe7..b0c333b8 100644 --- a/src/types.h +++ b/src/types.h @@ -304,7 +304,6 @@ ENABLE_FULL_OPERATORS_ON(Direction) ENABLE_INCR_OPERATORS_ON(PieceType) ENABLE_INCR_OPERATORS_ON(Piece) -ENABLE_INCR_OPERATORS_ON(Color) ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(File) ENABLE_INCR_OPERATORS_ON(Rank) From 9d3a2ecaa22cb55bddef0e932f2b2951cf6cacf5 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 7 Jul 2019 18:36:57 -0700 Subject: [PATCH 18/72] Bug fix: always choose shortest mate in multithread mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In current master, with the voting scheme the best thread selection may pick a non mate or not the shortest mate thread. This patch fixes this bug. Formatting suggestion by Jörg Oster. Related past pull requests: https://github.com/official-stockfish/Stockfish/pull/1074 https://github.com/official-stockfish/Stockfish/pull/1215 Passed a [-4..0] verification test with 3 threads: LLR: 2.95 (-2.94,2.94) [-4.00,0.00] Total: 57158 W: 11374 L: 11424 D: 34360 http://tests.stockfishchess.org/tests/view/5d22deb30ebc5925cf0caefd Closes https://github.com/official-stockfish/Stockfish/pull/2226 No functional change (in single threaded mode) ---------------------------------------------------- Comment by Jörg Oster Just one sample output to demonstrate the effect of this patch. 5 Threads, 1 GB Hash +---+---+---+---+---+---+---+---+ | r | | b | | | r | k | | +---+---+---+---+---+---+---+---+ | | | | n | | p | b | | +---+---+---+---+---+---+---+---+ | | | p | | p | | p | | +---+---+---+---+---+---+---+---+ | p | | | | | | P | | +---+---+---+---+---+---+---+---+ | P | p | | | B | | N | Q | +---+---+---+---+---+---+---+---+ | | q | | | | | P | | +---+---+---+---+---+---+---+---+ | | | R | | | P | | | +---+---+---+---+---+---+---+---+ | | | | R | | | K | | +---+---+---+---+---+---+---+---+ Fen: r1b2rk1/3n1pb1/2p1p1p1/p5P1/Pp2B1NQ/1q4P1/2R2P2/3R2K1 w - - 8 34 Key: 38B4CA1067D4F477 Checkers: ucinewgame isready readyok go mate 17 searchmoves d1d7 info depth 65 seldepth 36 multipv 1 score mate 18 nodes 785875935 nps 8650448 hashfull 1000 tbhits 0 time 90848 pv d1d7 c8d7 g4f6 g7f6 g5f6 b3a3 g1g2 a3a1 h4g5 a1f6 g5f6 e6e5 c2c1 d7h3 g2h3 a8a6 h3g2 c6c5 f6a6 g8g7 c1c5 f7f6 a6e6 f8f7 c5c8 f6f5 e4d5 g7h6 e6f7 f5f4 f7e7 f4f3 d5f3 b4b3 c8h8 info depth 63 seldepth 36 multipv 1 score mate 17 nodes 785875935 nps 8650448 hashfull 1000 tbhits 0 time 90848 pv d1d7 c8d7 g4f6 g7f6 g5f6 b3a3 g1g2 a3a1 h4g5 a1f6 g5f6 e6e5 c2c1 d7h3 g2h3 a8a6 c1d1 b4b3 h3g2 c6c5 f6a6 g8g7 d1d7 g7g8 a6f6 b3b2 e4g6 b2b1q g6f7 f8f7 f6f7 g8h8 f7g7 bestmove d1d7 ponder c8d7 --- src/search.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 09df1ac2..eda7f2f7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -277,7 +277,7 @@ void MainThread::search() { std::map votes; Value minScore = this->rootMoves[0].score; - // Find out minimum score and reset votes for moves which can be voted + // Find out minimum score for (Thread* th: Threads) minScore = std::min(minScore, th->rootMoves[0].score); @@ -287,7 +287,14 @@ void MainThread::search() { votes[th->rootMoves[0].pv[0]] += (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); - if (votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]) + if (bestThread->rootMoves[0].score >= VALUE_MATE_IN_MAX_PLY) + { + // Make sure we pick the shortest mate + if (th->rootMoves[0].score > bestThread->rootMoves[0].score) + bestThread = th; + } + else if ( th->rootMoves[0].score >= VALUE_MATE_IN_MAX_PLY + || votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]) bestThread = th; } } From d980d7c0d4b2efe7abe26bdd094859f6d888ee60 Mon Sep 17 00:00:00 2001 From: protonspring Date: Thu, 25 Jul 2019 16:27:46 -0600 Subject: [PATCH 19/72] Simplify weak lever STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 14844 W: 3347 L: 3212 D: 8285 http://tests.stockfishchess.org/tests/view/5d3a2d7b0ebc5925cf0f1632 LTC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 55261 W: 9374 L: 9309 D: 36578 http://tests.stockfishchess.org/tests/view/5d3a3d9e0ebc5925cf0f1786 Closes https://github.com/official-stockfish/Stockfish/pull/2257 bench: 3484124 --- src/pawns.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 9755c2ec..86c4b8ef 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -70,7 +70,7 @@ namespace { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); - Bitboard b, neighbours, stoppers, doubled, support, phalanx; + Bitboard neighbours, stoppers, doubled, support, phalanx; Bitboard lever, leverPush; Square s; bool opposed, backward, passed; @@ -145,11 +145,10 @@ namespace { score -= Doubled; } - // Penalize the unsupported and non passed pawns attacked twice by the enemy - b = ourPawns - & doubleAttackThem - & ~(e->pawnAttacks[Us] | e->passedPawns[Us]); - score -= WeakLever * popcount(b); + // Penalize our unsupported pawns attacked twice by enemy pawns + score -= WeakLever * popcount( ourPawns + & doubleAttackThem + & ~e->pawnAttacks[Us]); return score; } From 8152a74ab4f703717fdb493cf9059f89be9a4fba Mon Sep 17 00:00:00 2001 From: xoto10 Date: Tue, 30 Jul 2019 11:46:43 +0100 Subject: [PATCH 20/72] Tune search constants This is the result of a 200k tuning run at LTC: http://tests.stockfishchess.org/tests/view/5d3576b70ebc5925cf0e9e1e which passed quickly at LTC: LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 12954 W: 2280 L: 2074 D: 8600 http://tests.stockfishchess.org/tests/view/5d3ff3f70ebc5925cf0f87a2 STC failed, but second LTC at [0,4] passed easily: LLR: 2.96 (-2.94,2.94) [0.00,4.00] Total: 8004 W: 1432 L: 1252 D: 5320 http://tests.stockfishchess.org/tests/view/5d407cff0ebc5925cf0f9119 Further work? No doubt some of these changes produce most of the gain and some are neutral or even bad, so further testing on individual/groups of parameters changed here might show more gains. It does look like these tests might need to be at LTC though, so maybe not too practical to do. See the thread in the pull request for an interesting discussion: https://github.com/official-stockfish/Stockfish/pull/2260 Bench: 4024328 --- src/search.cpp | 54 +++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index eda7f2f7..6d1a66e5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -62,9 +62,9 @@ namespace { enum NodeType { NonPV, PV }; // Razor and futility margins - constexpr int RazorMargin = 600; + constexpr int RazorMargin = 661; Value futility_margin(Depth d, bool improving) { - return Value((175 - 50 * improving) * d / ONE_PLY); + return Value((168 - 51 * improving) * d / ONE_PLY); } // Reductions lookup table, initialized at startup @@ -72,7 +72,7 @@ namespace { Depth reduction(bool i, Depth d, int mn) { int r = Reductions[d / ONE_PLY] * Reductions[mn]; - return ((r + 512) / 1024 + (!i && r > 1024)) * ONE_PLY; + return ((r + 520) / 1024 + (!i && r > 999)) * ONE_PLY; } constexpr int futility_move_count(bool improving, int depth) { @@ -82,7 +82,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth depth) { int d = depth / ONE_PLY; - return d > 17 ? 0 : 29 * d * d + 138 * d - 134; + return d > 17 ? -8 : 22 * d * d + 151 * d - 140; } // Add a small random component to draw evaluations to avoid 3fold-blindness @@ -191,7 +191,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int(22.9 * std::log(i)); + Reductions[i] = int(23.4 * std::log(i)); } @@ -409,15 +409,15 @@ void Thread::search() { selDepth = 0; // Reset aspiration window starting size - if (rootDepth >= 5 * ONE_PLY) + if (rootDepth >= 4 * ONE_PLY) { Value previousScore = rootMoves[pvIdx].previousScore; - delta = Value(20); + delta = Value(23); alpha = std::max(previousScore - delta,-VALUE_INFINITE); beta = std::min(previousScore + delta, VALUE_INFINITE); // Adjust contempt based on root move's previousScore (dynamic contempt) - int dct = ct + 88 * previousScore / (abs(previousScore) + 200); + int dct = ct + 86 * previousScore / (abs(previousScore) + 176); contempt = (us == WHITE ? make_score(dct, dct / 2) : -make_score(dct, dct / 2)); @@ -512,12 +512,12 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (314 + 9 * (mainThread->previousScore - bestValue)) / 581.0; + double fallingEval = (354 + 10 * (mainThread->previousScore - bestValue)) / 692.0; fallingEval = clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 10 * ONE_PLY < completedDepth ? 1.95 : 1.0; - double reduction = (1.25 + mainThread->previousTimeReduction) / (2.25 * timeReduction); + timeReduction = lastBestMoveDepth + 9 * ONE_PLY < completedDepth ? 1.97 : 0.98; + double reduction = (1.36 + mainThread->previousTimeReduction) / (2.29 * timeReduction); // Use part of the gained time from a previous stable move for the current move for (Thread* th : Threads) @@ -796,9 +796,9 @@ namespace { // Step 9. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 23200 + && (ss-1)->statScore < 22661 && eval >= beta - && ss->staticEval >= beta - 36 * depth / ONE_PLY + 225 + && ss->staticEval >= beta - 33 * depth / ONE_PLY + 299 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -806,7 +806,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = ((823 + 67 * depth / ONE_PLY) / 256 + std::min(int(eval - beta) / 200, 3)) * ONE_PLY; + Depth R = ((835 + 70 * depth / ONE_PLY) / 256 + std::min(int(eval - beta) / 185, 3)) * ONE_PLY; ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0]; @@ -823,7 +823,7 @@ namespace { if (nullValue >= VALUE_MATE_IN_MAX_PLY) nullValue = beta; - if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 12 * ONE_PLY)) + if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 13 * ONE_PLY)) return nullValue; assert(!thisThread->nmpMinPly); // Recursive verification is not allowed @@ -849,7 +849,7 @@ namespace { && depth >= 5 * ONE_PLY && abs(beta) < VALUE_MATE_IN_MAX_PLY) { - Value raisedBeta = std::min(beta + 216 - 48 * improving, VALUE_INFINITE); + Value raisedBeta = std::min(beta + 191 - 46 * improving, VALUE_INFINITE); MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &thisThread->captureHistory); int probCutCount = 0; @@ -881,7 +881,7 @@ namespace { } // Step 11. Internal iterative deepening (~2 Elo) - if (depth >= 8 * ONE_PLY && !ttMove) + if (depth >= 7 * ONE_PLY && !ttMove) { search(pos, ss, alpha, beta, depth - 7 * ONE_PLY, cutNode); @@ -955,7 +955,7 @@ moves_loop: // When in check, search starts from here // then that move is singular and should be extended. To verify this we do // a reduced search on all the other moves but the ttMove and if the // result is lower than ttValue minus a margin then we will extend the ttMove. - if ( depth >= 8 * ONE_PLY + if ( depth >= 6 * ONE_PLY && move == ttMove && !rootNode && !excludedMove // Avoid recursive singular search @@ -976,7 +976,7 @@ moves_loop: // When in check, search starts from here extension = ONE_PLY; singularLMR++; - if (value < singularBeta - std::min(3 * depth / ONE_PLY, 39)) + if (value < singularBeta - std::min(4 * depth / ONE_PLY, 36)) singularLMR++; } @@ -1036,15 +1036,15 @@ moves_loop: // When in check, search starts from here lmrDepth /= ONE_PLY; // Countermoves based pruning (~20 Elo) - if ( lmrDepth < 3 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1) + if ( lmrDepth < 4 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1) && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) continue; // Futility pruning: parent node (~2 Elo) - if ( lmrDepth < 7 + if ( lmrDepth < 6 && !inCheck - && ss->staticEval + 256 + 200 * lmrDepth <= alpha) + && ss->staticEval + 250 + 211 * lmrDepth <= alpha) continue; // Prune moves with negative SEE (~10 Elo) @@ -1052,7 +1052,7 @@ moves_loop: // When in check, search starts from here continue; } else if ( (!givesCheck || !extension) - && !pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) // (~20 Elo) + && !pos.see_ge(move, Value(-199) * (depth / ONE_PLY))) // (~20 Elo) continue; } @@ -1119,7 +1119,7 @@ moves_loop: // When in check, search starts from here + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4000; + - 4729; // Reset statScore to zero if negative and most stats shows >= 0 if ( ss->statScore < 0 @@ -1129,10 +1129,10 @@ moves_loop: // When in check, search starts from here ss->statScore = 0; // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) - if (ss->statScore >= 0 && (ss-1)->statScore < 0) + if (ss->statScore >= -99 && (ss-1)->statScore < -116) r -= ONE_PLY; - else if ((ss-1)->statScore >= 0 && ss->statScore < 0) + else if ((ss-1)->statScore >= -117 && ss->statScore < -144) r += ONE_PLY; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) @@ -1402,7 +1402,7 @@ moves_loop: // When in check, search starts from here if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 128; + futilityBase = bestValue + 153; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, From fcee0ce6a39a28ffdfa4b1ed438b353a895edb6b Mon Sep 17 00:00:00 2001 From: joergoster Date: Thu, 4 Jul 2019 11:02:32 +0200 Subject: [PATCH 21/72] Revert "Improve multiPV mode" This reverts commit a8de07cc26999e2fef7298a63bfe349aaa4650fa. --- src/search.cpp | 8 +------- src/thread.h | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6d1a66e5..98419b20 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -341,7 +341,7 @@ void Thread::search() { bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; - multiPV = Options["MultiPV"]; + size_t multiPV = Options["MultiPV"]; // Pick integer skill levels, but non-deterministically round up or down // such that the average integer skill corresponds to the input floating point one. @@ -934,12 +934,6 @@ 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 46ddb495..c11d1787 100644 --- a/src/thread.h +++ b/src/thread.h @@ -59,7 +59,7 @@ public: Pawns::Table pawnsTable; Material::Table materialTable; - size_t pvIdx, multiPV, pvLast, shuffleExts; + size_t pvIdx, pvLast, shuffleExts; int selDepth, nmpMinPly; Color nmpColor; std::atomic nodes, tbHits, bestMoveChanges; From 66a3c2968b8552efce8c7e670d7ceabb28f9c1eb Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Wed, 14 Aug 2019 10:02:21 +0200 Subject: [PATCH 22/72] Tweak unsafe checks Remove mobility area for unsafe checks. Also separate the evaluation terms for unsafe checks and blockers for king with adjusted weights. STC: LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 124526 W: 28292 L: 27504 D: 68730 http://tests.stockfishchess.org/tests/view/5d5138290ebc5925cf1070c3 LTC: LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 84968 W: 14499 L: 14083 D: 56386 http://tests.stockfishchess.org/tests/view/5d527cfa0ebc5925cf107f93 Bench: 4139590 --- src/evaluate.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 7359eb92..edebb269 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -441,10 +441,6 @@ namespace { else unsafeChecks |= knightChecks; - // Unsafe or occupied checking squares will also be considered, as long as - // the square is in the attacker's mobility area. - unsafeChecks &= mobilityArea[Them]; - // Find the squares that opponent attacks in our king flank, and the squares // which are attacked twice in that flank. b1 = attackedBy[Them][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp; @@ -457,7 +453,8 @@ namespace { + 185 * popcount(kingRing[Us] & weak) - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) - 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING]) - + 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks) + + 148 * popcount(unsafeChecks) + + 98 * popcount(pos.blockers_for_king(Us)) - 873 * !pos.count(Them) - 6 * mg_value(score) / 8 + mg_value(mobility[Them] - mobility[Us]) From 7efc39d6833a0d999a7bf2f9b5629ceb246accd2 Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Wed, 14 Aug 2019 22:15:41 +0200 Subject: [PATCH 23/72] Assorted trivial cleanups (July 2019) No functional change --- AUTHORS | 2 ++ src/bitboard.h | 4 +++- src/evaluate.cpp | 17 ++++++++--------- src/pawns.cpp | 12 ++++++------ src/position.cpp | 2 +- src/search.cpp | 19 ++++++++++--------- src/syzygy/tbprobe.cpp | 2 +- 7 files changed, 31 insertions(+), 27 deletions(-) diff --git a/AUTHORS b/AUTHORS index 431bc838..207c5a85 100644 --- a/AUTHORS +++ b/AUTHORS @@ -11,6 +11,7 @@ Ajith Chandy Jose (ajithcj) Alain Savard (Rocky640) alayan-stk-2 Alexander Kure +Alexander Pagel (Lolligerhans) Ali AlZhrani (Cooffe) Andrew Grant (AndyGrant) Andrey Neporada (nepal) @@ -82,6 +83,7 @@ Lub van den Berg (ElbertoOne) Luca Brivio (lucabrivio) Lucas Braesch (lucasart) Lyudmil Antonov (lantonov) +Maciej Żenczykowski (zenczykowski) Matthew Lai (matthewlai) Matthew Sullivan Mark Tenzer (31m059) diff --git a/src/bitboard.h b/src/bitboard.h index 7a16597d..477b1655 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -377,6 +377,8 @@ inline Square pop_lsb(Bitboard* b) { /// 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 frontmost_sq(Color c, Bitboard b) { + return c == WHITE ? msb(b) : lsb(b); +} #endif // #ifndef BITBOARD_H_INCLUDED diff --git a/src/evaluate.cpp b/src/evaluate.cpp index edebb269..05fa45bc 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -505,9 +505,6 @@ namespace { // Enemies not strongly protected and under our attack weak = pos.pieces(Them) & ~stronglyProtected & attackedBy[Us][ALL_PIECES]; - // Safe or protected squares - safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES]; - // Bonus according to the kind of attacking pieces if (defended | weak) { @@ -544,6 +541,14 @@ namespace { score += RestrictedPiece * popcount(b); + // Protected or unattacked squares + safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES]; + + // Bonus for attacking enemy pieces with our relatively safe pawns + b = pos.pieces(Us, PAWN) & safe; + b = pawn_attacks_bb(b) & nonPawnEnemies; + score += ThreatBySafePawn * popcount(b); + // 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(); @@ -555,12 +560,6 @@ namespace { b = pawn_attacks_bb(b) & nonPawnEnemies; score += ThreatByPawnPush * popcount(b); - // Our safe or protected pawns - b = pos.pieces(Us, PAWN) & safe; - - b = pawn_attacks_bb(b) & nonPawnEnemies; - score += ThreatBySafePawn * popcount(b); - // Bonus for threats on the next moves against enemy queen if (pos.count(Them) == 1) { diff --git a/src/pawns.cpp b/src/pawns.cpp index 86c4b8ef..5df175b2 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -52,8 +52,8 @@ 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) + // is behind our king. Note that UnblockedStorm[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(-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) }, @@ -196,10 +196,10 @@ 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, frontmost_sq(Them, b)) : RANK_1; + int ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; b = theirPawns & file_bb(f); - Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; + int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; int d = std::min(f, ~f); bonus += make_score(ShelterStrength[d][ourRank], 0); @@ -234,7 +234,7 @@ Score Entry::do_king_safety(const Position& pos) { else while (pawns) minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns))); - Score shelter = make_score(-VALUE_INFINITE, VALUE_ZERO); + Score shelter = make_score(-VALUE_INFINITE, 0); evaluate_shelter(pos, ksq, shelter); // If we can castle use the bonus after the castling if it is bigger @@ -244,7 +244,7 @@ Score Entry::do_king_safety(const Position& pos) { if (pos.can_castle(Us | QUEEN_SIDE)) evaluate_shelter(pos, relative_square(Us, SQ_C1), shelter); - return shelter - make_score(VALUE_ZERO, 16 * minPawnDist); + return shelter - make_score(0, 16 * minPawnDist); } // Explicit template instantiation diff --git a/src/position.cpp b/src/position.cpp index fbde810b..4358f968 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -882,7 +882,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { if (end >= 4) { StateInfo* stp = st->previous->previous; - for (int i=4; i <= end; i += 2) + for (int i = 4; i <= end; i += 2) { stp = stp->previous->previous; if (stp->key == st->key) diff --git a/src/search.cpp b/src/search.cpp index 98419b20..b154c6d9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -102,15 +102,16 @@ namespace { Move best = MOVE_NONE; }; - // Breadcrumbs are used to mark nodes as being searched by a given thread. + // 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. + // ThreadHolding structure 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 by the constructor, and unmarked upon leaving that loop by the destructor. struct ThreadHolding { explicit ThreadHolding(Thread* thisThread, Key posKey, int ply) { location = ply < 8 ? &breadcrumbs[posKey & (breadcrumbs.size() - 1)] : nullptr; @@ -118,7 +119,7 @@ namespace { owning = false; if (location) { - // see if another already marked this location, if not, mark it ourselves. + // See if another already marked this location, if not, mark it ourselves Thread* tmp = (*location).thread.load(std::memory_order_relaxed); if (tmp == nullptr) { @@ -133,7 +134,7 @@ namespace { } ~ThreadHolding() { - if (owning) // free the marked location. + if (owning) // Free the marked location (*location).thread.store(nullptr, std::memory_order_relaxed); } @@ -647,9 +648,9 @@ namespace { // 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; + (ss+4)->statScore = 0; else - (ss + 2)->statScore = 0; + (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 @@ -680,7 +681,7 @@ namespace { // Extra penalty for early quiet moves of the previous ply if ((ss-1)->moveCount <= 2 && !pos.captured_piece()) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY)); + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY)); } // Penalty for a quiet ttMove that fails low else if (!pos.capture_or_promotion(ttMove)) @@ -908,7 +909,7 @@ moves_loop: // When in check, search starts from here moveCountPruning = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); - // Mark this node as being searched. + // Mark this node as being searched ThreadHolding th(thisThread, posKey, ss->ply); // Step 12. Loop through all pseudo-legal moves until no moves remain diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 90c86388..10864744 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -367,7 +367,7 @@ TBTable::TBTable(const std::string& code) : TBTable() { hasPawns = pos.pieces(PAWN); hasUniquePieces = false; - for (Color c : {WHITE, BLACK}) + for (Color c : { WHITE, BLACK }) for (PieceType pt = PAWN; pt < KING; ++pt) if (popcount(pos.pieces(c, pt)) == 1) hasUniquePieces = true; From d4dca9187e83dde29be8d76ca50ff53d14199ce9 Mon Sep 17 00:00:00 2001 From: Jean Gauthier Date: Wed, 14 Aug 2019 08:44:21 -0400 Subject: [PATCH 24/72] Slight speep up fetching the endgame table Replace calls to count(key) + operator[key] with a single call to find(key). Replace the std::map with std::unordered_map which provide O(1) access, although the map has a really small number of objects. Test with [0..4] failed yellow: TC 10+0.1 SPRT elo0: 0.00 alpha: 0.05 elo1: 4.00 beta: 0.05 LLR -2.96 [-2.94,2.94] (rejected) Elo 1.01 [-0.87,3.08] (95%) LOS 85.3% Games 71860 [w:22.3%, l:22.2%, d:55.5%] http://tests.stockfishchess.org/tests/view/5d5432210ebc5925cf109d61 Closes https://github.com/official-stockfish/Stockfish/pull/2269 No functional change --- src/endgame.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/endgame.h b/src/endgame.h index d0a5a97e..e29f8777 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -21,7 +21,7 @@ #ifndef ENDGAME_H_INCLUDED #define ENDGAME_H_INCLUDED -#include +#include #include #include #include @@ -98,7 +98,7 @@ struct Endgame : public EndgameBase { namespace Endgames { template using Ptr = std::unique_ptr>; - template using Map = std::map>; + template using Map = std::unordered_map>; extern std::pair, Map> maps; @@ -119,7 +119,8 @@ namespace Endgames { template const EndgameBase* probe(Key key) { - return map().count(key) ? map()[key].get() : nullptr; + auto it = map().find(key); + return it != map().end() ? it->second.get() : nullptr; } } From 18279b24fc76bb6eaf6ac01f3032b1b90da5dabb Mon Sep 17 00:00:00 2001 From: protonspring Date: Tue, 20 Aug 2019 19:52:18 -0600 Subject: [PATCH 25/72] Tuned Futility Equation @Vizvezdenec array suggested that alternate values may be better than current master (see pull request #2270 ). I tuned some linear equations to more closely represent his values and it passed. These futility values seem quite sensitive, so perhaps additional Elo improvements can be found here. STC LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 12257 W: 2820 L: 2595 D: 6842 http://tests.stockfishchess.org/tests/view/5d5b2f360ebc5925cf1111ac LTC LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 20273 W: 3497 L: 3264 D: 13512 http://tests.stockfishchess.org/tests/view/5d5c0d250ebc5925cf111ac3 Closes https://github.com/official-stockfish/Stockfish/pull/2272 ------------------------------------------ How to continue from there ? a) we can try a simpler version for the futility margin, this would be a simplification : margin = 188 * (depth - improving) b) on the other direction, we can try a complexification by trying again to gain Elo with an complete array of futility values. ------------------------------------------ Bench: 4330402 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index b154c6d9..69488ad5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -64,7 +64,7 @@ namespace { // Razor and futility margins constexpr int RazorMargin = 661; Value futility_margin(Depth d, bool improving) { - return Value((168 - 51 * improving) * d / ONE_PLY); + return Value(198 * (d / ONE_PLY) - 178 * improving); } // Reductions lookup table, initialized at startup From 10d2ebc6ae7aad8ee3a48aac41ad00321b1b7e78 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 21 Aug 2019 08:30:48 +0300 Subject: [PATCH 26/72] Late move reduction, captures and CUT nodes Expand of Stefan Geschwentner's original idea: we always do LMR for captures at cutnodes. Passed STC http://tests.stockfishchess.org/tests/view/5d5b2f8e0ebc5925cf1111b8 LLR: 2.96 (-2.94,2.94) [0.50,4.50] Total: 36026 W: 8122 L: 7779 D: 20125 Passed LTC http://tests.stockfishchess.org/tests/view/5d5b40c80ebc5925cf111353 LLR: 3.22 (-2.94,2.94) [0.00,3.50] Total: 133502 W: 22508 L: 21943 D: 89051 Closes https://github.com/official-stockfish/Stockfish/pull/2273 Bench: 3494372 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 69488ad5..7f421b9c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1074,7 +1074,8 @@ moves_loop: // When in check, search starts from here && moveCount > 1 + 3 * rootNode && ( !captureOrPromotion || moveCountPruning - || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha)) + || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha + || cutNode)) { Depth r = reduction(improving, depth, moveCount); From a016626825972d546d2e4ef6abd2e55f7bf2f3dc Mon Sep 17 00:00:00 2001 From: protonspring Date: Thu, 22 Aug 2019 08:27:26 -0600 Subject: [PATCH 27/72] Simplify futility equation This is a functional simplification. The 178 constant for the futility equation in master can be removed. STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 42626 W: 9508 L: 9428 D: 23690 http://tests.stockfishchess.org/tests/view/5d5d4e320ebc5925cf11254e LTC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 26182 W: 4432 L: 4320 D: 17430 http://tests.stockfishchess.org/tests/view/5d5df70d0ebc5925cf112fee Closes https://github.com/official-stockfish/Stockfish/pull/2278 Bench: 3985701 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 7f421b9c..7a16ef8c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -64,7 +64,7 @@ namespace { // Razor and futility margins constexpr int RazorMargin = 661; Value futility_margin(Depth d, bool improving) { - return Value(198 * (d / ONE_PLY) - 178 * improving); + return Value(198 * (d / ONE_PLY - improving)); } // Reductions lookup table, initialized at startup From 3984b8f8f0e1f53c737020c936f2a8372029545d Mon Sep 17 00:00:00 2001 From: protonspring Date: Mon, 12 Aug 2019 08:42:28 -0600 Subject: [PATCH 28/72] Consolidate CastlingSide and CastlingRights MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a non-functional simplification that removes CastlingSide and implements the functionality in CastlingRights (thanks to Jörg Oster for a comment on the first version of this patch). STC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 53854 W: 12077 L: 12019 D: 29758 http://tests.stockfishchess.org/tests/view/5d517b940ebc5925cf107474 Closes https://github.com/official-stockfish/Stockfish/pull/2265 No functional change --- src/movegen.cpp | 6 +++--- src/pawns.cpp | 4 ++-- src/position.cpp | 17 ++++++++--------- src/position.h | 12 ++++++------ src/types.h | 16 +++++++--------- 5 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 4c609352..fc99ec26 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -219,8 +219,8 @@ namespace { template ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) { - constexpr CastlingRight OO = Us | KING_SIDE; - constexpr CastlingRight OOO = Us | QUEEN_SIDE; + constexpr CastlingRights OO = Us & KING_SIDE; + constexpr CastlingRights OOO = Us & QUEEN_SIDE; constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations moveList = generate_pawn_moves(pos, moveList, target); @@ -236,7 +236,7 @@ namespace { while (b) *moveList++ = make_move(ksq, pop_lsb(&b)); - if (Type != CAPTURES && pos.can_castle(CastlingRight(OO | OOO))) + if (Type != CAPTURES && pos.can_castle(CastlingRights(OO | OOO))) { if (!pos.castling_impeded(OO) && pos.can_castle(OO)) *moveList++ = make(ksq, pos.castling_rook_square(OO)); diff --git a/src/pawns.cpp b/src/pawns.cpp index 5df175b2..1cacc1e3 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -238,10 +238,10 @@ Score Entry::do_king_safety(const Position& pos) { 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)) + if (pos.can_castle(Us & KING_SIDE)) evaluate_shelter(pos, relative_square(Us, SQ_G1), shelter); - if (pos.can_castle(Us | QUEEN_SIDE)) + if (pos.can_castle(Us & QUEEN_SIDE)) evaluate_shelter(pos, relative_square(Us, SQ_C1), shelter); return shelter - make_score(0, 16 * minPawnDist); diff --git a/src/position.cpp b/src/position.cpp index 4358f968..db66f416 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -330,16 +330,15 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th void Position::set_castling_right(Color c, Square rfrom) { Square kfrom = square(c); - CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE; - CastlingRight cr = (c | cs); + CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE: QUEEN_SIDE); st->castlingRights |= cr; castlingRightsMask[kfrom] |= cr; castlingRightsMask[rfrom] |= cr; castlingRookSquare[cr] = 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); + Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1); + Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1); castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto) & ~(square_bb(kfrom) | rfrom); @@ -1300,14 +1299,14 @@ bool Position::pos_is_ok() const { } for (Color c : { WHITE, BLACK }) - for (CastlingSide s : {KING_SIDE, QUEEN_SIDE}) + for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE}) { - if (!can_castle(c | s)) + if (!can_castle(cr)) continue; - if ( piece_on(castlingRookSquare[c | s]) != make_piece(c, ROOK) - || castlingRightsMask[castlingRookSquare[c | s]] != (c | s) - || (castlingRightsMask[square(c)] & (c | s)) != (c | s)) + if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK) + || castlingRightsMask[castlingRookSquare[cr]] != (cr) + || (castlingRightsMask[square(c)] & (cr)) != (cr)) assert(0 && "pos_is_ok: Castling"); } diff --git a/src/position.h b/src/position.h index 2106414b..a0a9a306 100644 --- a/src/position.h +++ b/src/position.h @@ -100,9 +100,9 @@ public: // Castling int castling_rights(Color c) const; - bool can_castle(CastlingRight cr) const; - bool castling_impeded(CastlingRight cr) const; - Square castling_rook_square(CastlingRight cr) const; + bool can_castle(CastlingRights cr) const; + bool castling_impeded(CastlingRights cr) const; + Square castling_rook_square(CastlingRights cr) const; // Checking Bitboard checkers() const; @@ -268,7 +268,7 @@ 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 { +inline bool Position::can_castle(CastlingRights cr) const { return st->castlingRights & cr; } @@ -276,11 +276,11 @@ inline int Position::castling_rights(Color c) const { return st->castlingRights & (c == WHITE ? WHITE_CASTLING : BLACK_CASTLING); } -inline bool Position::castling_impeded(CastlingRight cr) const { +inline bool Position::castling_impeded(CastlingRights cr) const { return byTypeBB[ALL_PIECES] & castlingPath[cr]; } -inline Square Position::castling_rook_square(CastlingRight cr) const { +inline Square Position::castling_rook_square(CastlingRights cr) const { return castlingRookSquare[cr]; } diff --git a/src/types.h b/src/types.h index b0c333b8..3559d72b 100644 --- a/src/types.h +++ b/src/types.h @@ -131,19 +131,17 @@ enum Color { WHITE, BLACK, COLOR_NB = 2 }; -enum CastlingSide { - KING_SIDE, QUEEN_SIDE, CASTLING_SIDE_NB = 2 -}; - -enum CastlingRight { +enum CastlingRights { NO_CASTLING, WHITE_OO, WHITE_OOO = WHITE_OO << 1, BLACK_OO = WHITE_OO << 2, BLACK_OOO = WHITE_OO << 3, - WHITE_CASTLING = WHITE_OO | WHITE_OOO, - BLACK_CASTLING = BLACK_OO | BLACK_OOO, + KING_SIDE = WHITE_OO | BLACK_OO, + QUEEN_SIDE = WHITE_OOO | BLACK_OOO, + WHITE_CASTLING = WHITE_OO | WHITE_OOO, + BLACK_CASTLING = BLACK_OO | BLACK_OOO, ANY_CASTLING = WHITE_CASTLING | BLACK_CASTLING, CASTLING_RIGHT_NB = 16 @@ -363,8 +361,8 @@ constexpr Piece operator~(Piece pc) { return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT } -constexpr CastlingRight operator|(Color c, CastlingSide s) { - return CastlingRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c)); +constexpr CastlingRights operator&(Color c, CastlingRights cr) { + return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr); } constexpr Value mate_in(int ply) { From d799529b4843edd7630901a16c48a13d1fb50565 Mon Sep 17 00:00:00 2001 From: protonspring Date: Sat, 24 Aug 2019 08:16:20 +0200 Subject: [PATCH 29/72] Improve signature of evaluate_shelter() Remove one parameter in function evaluate_shelter(), making all comparisons for castled/uncastled shelter locally in do_king_safety(). Also introduce BlockedStorm penalty. Passed non-regression test at STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 65864 W: 14630 L: 14596 D: 36638 http://tests.stockfishchess.org/tests/view/5d5fc80c0ebc5939d09f0acc No functional change --- src/pawns.cpp | 37 +++++++++++++++++++++---------------- src/pawns.h | 2 +- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 1cacc1e3..33e859e5 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -33,6 +33,7 @@ namespace { // Pawn penalties constexpr Score Backward = S( 9, 24); + constexpr Score BlockedStorm = S(82, 82); constexpr Score Doubled = S(11, 56); constexpr Score Isolated = S( 5, 15); constexpr Score WeakLever = S( 0, 56); @@ -182,7 +183,7 @@ Entry* probe(const Position& pos) { /// penalty for a king, looking at the king file and the two closest files. template -void Entry::evaluate_shelter(const Position& pos, Square ksq, Score& shelter) { +Score Entry::evaluate_shelter(const Position& pos, Square ksq) { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); @@ -205,13 +206,12 @@ void Entry::evaluate_shelter(const Position& pos, Square ksq, Score& shelter) { bonus += make_score(ShelterStrength[d][ourRank], 0); if (ourRank && (ourRank == theirRank - 1)) - bonus -= make_score(82 * (theirRank == RANK_3), 82 * (theirRank == RANK_3)); + bonus -= BlockedStorm * int(theirRank == RANK_3); else bonus -= make_score(UnblockedStorm[d][theirRank], 0); } - if (mg_value(bonus) > mg_value(shelter)) - shelter = bonus; + return bonus; } @@ -225,26 +225,31 @@ Score Entry::do_king_safety(const Position& pos) { kingSquares[Us] = ksq; castlingRights[Us] = pos.castling_rights(Us); + Score shelters[3] = { evaluate_shelter(pos, ksq), + make_score(-VALUE_INFINITE, 0), + make_score(-VALUE_INFINITE, 0) }; + + // If we can castle use the bonus after castling if it is bigger + if (pos.can_castle(Us & KING_SIDE)) + shelters[1] = evaluate_shelter(pos, relative_square(Us, SQ_G1)); + + if (pos.can_castle(Us & QUEEN_SIDE)) + shelters[2] = evaluate_shelter(pos, relative_square(Us, SQ_C1)); + + for (int i : {1, 2}) + if (mg_value(shelters[i]) > mg_value(shelters[0])) + shelters[0] = shelters[i]; + + // In endgame we like to bring our king near our closest pawn Bitboard pawns = pos.pieces(Us, PAWN); int minPawnDist = pawns ? 8 : 0; if (pawns & PseudoAttacks[KING][ksq]) minPawnDist = 1; - else while (pawns) minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns))); - Score shelter = make_score(-VALUE_INFINITE, 0); - 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)) - evaluate_shelter(pos, relative_square(Us, SQ_G1), shelter); - - if (pos.can_castle(Us & QUEEN_SIDE)) - evaluate_shelter(pos, relative_square(Us, SQ_C1), shelter); - - return shelter - make_score(0, 16 * minPawnDist); + return shelters[0] - make_score(0, 16 * minPawnDist); } // Explicit template instantiation diff --git a/src/pawns.h b/src/pawns.h index 1f930988..4c041716 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -49,7 +49,7 @@ struct Entry { Score do_king_safety(const Position& pos); template - void evaluate_shelter(const Position& pos, Square ksq, Score& shelter); + Score evaluate_shelter(const Position& pos, Square ksq); Key key; Score scores[COLOR_NB]; From 0e295fee25a41962d234c0833e1f7ca29e4c2189 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Wed, 14 Aug 2019 20:46:09 -0400 Subject: [PATCH 30/72] NMP Tweaks Tweak again the null move pruning preconditions. STC: LLR: 2.96 (-2.94,2.94) [0.50,4.50] Total: 19675 W: 4430 L: 4169 D: 11076 http://tests.stockfishchess.org/tests/view/5d52bc0e0ebc5925cf108300 LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 73895 W: 12496 L: 12114 D: 49285 http://tests.stockfishchess.org/tests/view/5d52dcbc0ebc5925cf108552 Closes https://github.com/official-stockfish/Stockfish/pull/2268 Bench: 3690065 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 7a16ef8c..f0fa6f05 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -799,7 +799,8 @@ namespace { && (ss-1)->currentMove != MOVE_NULL && (ss-1)->statScore < 22661 && eval >= beta - && ss->staticEval >= beta - 33 * depth / ONE_PLY + 299 + && eval >= ss->staticEval + && ss->staticEval >= beta - 33 * depth / ONE_PLY + 299 - improving * 30 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) From 8fec8834715a440ac18e24e130888c2c60bab352 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 25 Aug 2019 21:45:58 +0200 Subject: [PATCH 31/72] Tweak Late Move Reduction at root Maintain best move counter at the root and allow there only moves which has a counter of zero for Late Move Reduction. For compensation only the first three moves are excluded from Late Move Reduction per default instead the first four moves. What we can further do: - here we use a simple counting scheme but perhaps some aging to fade out early iterations could be helpful - use the best move counter also at inner nodes for LMR and/or pruning STC: LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 17414 W: 3984 L: 3733 D: 9697 http://tests.stockfishchess.org/tests/view/5d6234bb0ebc5939d09f2aa2 LTC: LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 38058 W: 6448 L: 6166 D: 25444 http://tests.stockfishchess.org/tests/view/5d62681a0ebc5939d09f2f27 Closes https://github.com/official-stockfish/Stockfish/pull/2282 Bench: 3568210 --- src/search.cpp | 6 +++++- src/search.h | 1 + src/thread.cpp | 9 +++++++++ src/thread.h | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f0fa6f05..28cbffd6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -472,7 +472,10 @@ void Thread::search() { ++failedHighCnt; } else + { + ++rootMoves[pvIdx].bestMoveCount; break; + } delta += delta / 4 + 5; @@ -1072,7 +1075,8 @@ 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 + 3 * rootNode + && moveCount > 1 + 2 * rootNode + && (!rootNode || thisThread->best_move_count(move) == 0) && ( !captureOrPromotion || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha diff --git a/src/search.h b/src/search.h index 24c58d30..c77ca3ad 100644 --- a/src/search.h +++ b/src/search.h @@ -70,6 +70,7 @@ struct RootMove { Value previousScore = -VALUE_INFINITE; int selDepth = 0; int tbRank = 0; + int bestMoveCount = 0; Value tbScore; std::vector pv; }; diff --git a/src/thread.cpp b/src/thread.cpp index e5043b6e..5165fd90 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -52,6 +52,15 @@ Thread::~Thread() { stdThread.join(); } +/// Thread::bestMoveCount(Move move) return best move counter for the given root move + +int Thread::best_move_count(Move move) { + + auto rm = std::find(rootMoves.begin() + pvIdx, + rootMoves.begin() + pvLast, move); + + return rm != rootMoves.begin() + pvLast ? rm->bestMoveCount : 0; +} /// Thread::clear() reset histories, usually before a new game diff --git a/src/thread.h b/src/thread.h index c11d1787..ed427b10 100644 --- a/src/thread.h +++ b/src/thread.h @@ -56,6 +56,7 @@ public: void idle_loop(); void start_searching(); void wait_for_search_finished(); + int best_move_count(Move move); Pawns::Table pawnsTable; Material::Table materialTable; From 61f44ce57864f866de5d26a3402a9ad135e17c6d Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Wed, 11 Sep 2019 13:46:08 +0200 Subject: [PATCH 32/72] Update reverse move stats For a good quiet non-pawn move consider the reverse move as bad and update the main history with a negative stat bonus. STC: LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 19292 W: 4401 L: 4141 D: 10750 http://tests.stockfishchess.org/tests/view/5d7751d50ebc594e7864973c LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 111952 W: 18762 L: 18275 D: 74915 http://tests.stockfishchess.org/tests/view/5d7771cf0ebc594e786498fa Closes https://github.com/official-stockfish/Stockfish/pull/2294 Bench: 3914238 --- src/search.cpp | 5 ++++- src/types.h | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 28cbffd6..f8535a5f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1113,7 +1113,7 @@ moves_loop: // When in check, search starts from here // castling moves, because they are coded as "king captures rook" and // hence break make_move(). (~5 Elo) else if ( type_of(move) == NORMAL - && !pos.see_ge(make_move(to_sq(move), from_sq(move)))) + && !pos.see_ge(reverse_move(move))) r -= 2 * ONE_PLY; ss->statScore = thisThread->mainHistory[us][from_to(move)] @@ -1603,6 +1603,9 @@ moves_loop: // When in check, search starts from here thisThread->mainHistory[us][from_to(move)] << bonus; update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus); + if (type_of(pos.moved_piece(move)) != PAWN) + thisThread->mainHistory[us][from_to(reverse_move(move))] << -bonus; + if (is_ok((ss-1)->currentMove)) { Square prevSq = to_sq((ss-1)->currentMove); diff --git a/src/types.h b/src/types.h index 3559d72b..934f884e 100644 --- a/src/types.h +++ b/src/types.h @@ -442,6 +442,10 @@ constexpr Move make_move(Square from, Square to) { return Move((from << 6) + to); } +constexpr Move reverse_move(Move m) { + return make_move(to_sq(m), from_sq(m)); +} + template constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) { return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); From 270b241ec12ad3a32634a846b87ec9dc08fa2730 Mon Sep 17 00:00:00 2001 From: protonspring Date: Wed, 11 Sep 2019 12:36:58 -0600 Subject: [PATCH 33/72] Simplify Weak Lever This is a simplification that integrated WeakLever into doubled pawns. Since we already check for !support for Doubled pawns, it is trivial to check for weak lever by just checking more_than_one(lever). We also introduce the Score * bool operation overload to remove some casts in the code. STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 26757 W: 5842 L: 5731 D: 15184 http://tests.stockfishchess.org/tests/view/5d77ee220ebc5902d384e5a4 Closes https://github.com/official-stockfish/Stockfish/pull/2295 No functional change --- src/pawns.cpp | 18 +++++++----------- src/types.h | 5 +++++ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 33e859e5..1ae65bf1 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -71,10 +71,10 @@ namespace { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); - Bitboard neighbours, stoppers, doubled, support, phalanx; + Bitboard neighbours, stoppers, support, phalanx; Bitboard lever, leverPush; Square s; - bool opposed, backward, passed; + bool opposed, backward, passed, doubled; Score score = SCORE_ZERO; const Square* pl = pos.squares(Us); @@ -137,20 +137,16 @@ namespace { } else if (!neighbours) - score -= Isolated + WeakUnopposed * int(!opposed); + score -= Isolated + WeakUnopposed * !opposed; else if (backward) - score -= Backward + WeakUnopposed * int(!opposed); + score -= Backward + WeakUnopposed * !opposed; - if (doubled && !support) - score -= Doubled; + if (!support) + score -= Doubled * doubled + + WeakLever * more_than_one(lever); } - // Penalize our unsupported pawns attacked twice by enemy pawns - score -= WeakLever * popcount( ourPawns - & doubleAttackThem - & ~e->pawnAttacks[Us]); - return score; } diff --git a/src/types.h b/src/types.h index 934f884e..c77d8040 100644 --- a/src/types.h +++ b/src/types.h @@ -345,6 +345,11 @@ inline Score operator*(Score s, int i) { return result; } +/// Multiplication of a Score by an boolean +inline Score operator*(Score s, bool b) { + return Score(int(s) * int(b)); +} + constexpr Color operator~(Color c) { return Color(c ^ BLACK); // Toggle color } From 36e4a86c08a4b79965d819b7893709245cad840a Mon Sep 17 00:00:00 2001 From: xoto10 Date: Thu, 12 Sep 2019 04:29:23 +0100 Subject: [PATCH 34/72] Bonus for rook on same file as their queen This patch creates a simple bonus for a rook that is on the same file as the opponent's queen. STC 10+0.1 th 1 : LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 45609 W: 10120 L: 9733 D: 25756 http://tests.stockfishchess.org/tests/view/5d79895a0ebc5902d385484a LTC 60+0.6 th 1 : LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 51651 W: 8606 L: 8288 D: 34757 http://tests.stockfishchess.org/tests/view/5d79a0850ebc5902d3854d27 Many thanks to @noobpwnftw for providing the extra cpu resources for fishtest, which led to me doing these tests. Closes https://github.com/official-stockfish/Stockfish/pull/2297 Bench: 4024461 --- src/evaluate.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 05fa45bc..cb1ad1f4 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -140,6 +140,7 @@ namespace { constexpr Score PawnlessFlank = S( 17, 95); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RookOnPawn = S( 10, 32); + constexpr Score RookOnQueenFile = S( 11, 4); constexpr Score SliderOnQueen = S( 59, 18); constexpr Score ThreatByKing = S( 24, 89); constexpr Score ThreatByPawnPush = S( 48, 39); @@ -346,6 +347,10 @@ namespace { if (relative_rank(Us, s) >= RANK_5) score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]); + // Bonus for rook on same file as their queen + if (file_bb(s) & pos.pieces(Them, QUEEN)) + score += RookOnQueenFile; + // Bonus for rook on an open or semi-open file if (pos.is_on_semiopen_file(Us, s)) score += RookOnFile[bool(pos.is_on_semiopen_file(Them, s))]; From 8aecf2698184babce57ccfd2ba5948342e29c325 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Thu, 12 Sep 2019 06:43:04 +0300 Subject: [PATCH 35/72] Scale down complexity for almost unwinnable endgames This patch greatly scales down complexity of endgames when the following conditions are all true together: - pawns are all on one flank - stronger side king is not outflanking weaker side - no passed pawns are present This should improve stockfish evaluation of obvious draws 4 vs 3, 3 vs 2 and 2 vs 1 pawns in rook/queen/knight/bishop single flank endgames where strong side can not make progress. passed STC LLR: 2.94 (-2.94,2.94) [0.50,4.50] Total: 15843 W: 3601 L: 3359 D: 8883 passed LTC LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 121275 W: 20107 L: 19597 D: 81571 Closes https://github.com/official-stockfish/Stockfish/pull/2298 Bench: 3954190 ========================== How to continue from there? a) This could be a powerful idea for refining some parts of the evaluation function, a bit like when we try quadratics or other equations to emphasize certain situations (xoto10). b) Some other combinaison values for this bonus can be done further, or overall retuning of weight and offset while keeping the formula simple. --- src/evaluate.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index cb1ad1f4..73e2144c 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -725,12 +725,17 @@ namespace { bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide) && (pos.pieces(PAWN) & KingSide); + bool almostUnwinnable = !pe->passed_count() + && outflanking < 0 + && !pawnsOnBothFlanks; + // Compute the initiative bonus for the attacking side int complexity = 9 * pe->passed_count() + 11 * pos.count() + 9 * outflanking + 18 * pawnsOnBothFlanks + 49 * !pos.non_pawn_material() + - 36 * almostUnwinnable -103 ; // Now apply the bonus: note that we find the attacking side by extracting From db00e1625eb46517f61085ffd3bcd28779e71220 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 12 Sep 2019 21:25:58 +0200 Subject: [PATCH 36/72] Add sse4 if bmi2 is enabled The only change done to the Makefile to get a somewhat faster binary as discussed in #2291 is to add -msse4 to the compile options of the bmi2 build. Since all processors supporting bmi2 also support sse4 this can be done easily. It is a useful step to avoid sending around custom and poorly tested builds. The speedup isn't enough to pass [0,4] but it is roughly 1.15Elo and a LOS of 90%: LLR: -2.95 (-2.94,2.94) [0.00,4.00] Total: 93009 W: 20519 L: 20316 D: 52174 Also rewrite the documentation for the user when using `make --help`, so that the order of architectures for x86-64 has the more performant build one on top. Closes https://github.com/official-stockfish/Stockfish/pull/2300 No functional change --- src/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Makefile b/src/Makefile index 285d314e..6deb0e27 100644 --- a/src/Makefile +++ b/src/Makefile @@ -328,7 +328,7 @@ endif ifeq ($(pext),yes) CXXFLAGS += -DUSE_PEXT ifeq ($(comp),$(filter $(comp),gcc clang mingw)) - CXXFLAGS += -mbmi2 + CXXFLAGS += -msse4 -mbmi2 endif endif @@ -379,9 +379,9 @@ help: @echo "" @echo "Supported archs:" @echo "" - @echo "x86-64 > x86 64-bit" - @echo "x86-64-modern > x86 64-bit with popcnt support" - @echo "x86-64-bmi2 > x86 64-bit with pext support" + @echo "x86-64-bmi2 > x86 64-bit with pext support (also enables SSE4)" + @echo "x86-64-modern > x86 64-bit with popcnt support (also enables SSE3)" + @echo "x86-64 > x86 64-bit generic" @echo "x86-32 > x86 32-bit with SSE support" @echo "x86-32-old > x86 32-bit fall back for old hardware" @echo "ppc-64 > PPC 64-bit" From 8a04b3a13cdbd2153287050e65aaa3530ad0104c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sat, 14 Sep 2019 07:33:48 +0200 Subject: [PATCH 37/72] Update Makefile documentation Follow-up to previous commit. Update the documentation for the user when using `make`, to show the preferred bmi2 compile in the advanced examples section. Note: I made a mistake in the previous commit comment, the documentation is shown when using `make` or `make help`, not `make --help`. No functional change --- src/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 6deb0e27..70246f56 100644 --- a/src/Makefile +++ b/src/Makefile @@ -382,7 +382,7 @@ help: @echo "x86-64-bmi2 > x86 64-bit with pext support (also enables SSE4)" @echo "x86-64-modern > x86 64-bit with popcnt support (also enables SSE3)" @echo "x86-64 > x86 64-bit generic" - @echo "x86-32 > x86 32-bit with SSE support" + @echo "x86-32 > x86 32-bit (also enables SSE)" @echo "x86-32-old > x86 32-bit fall back for old hardware" @echo "ppc-64 > PPC 64-bit" @echo "ppc-32 > PPC 32-bit" @@ -405,7 +405,7 @@ help: @echo "Advanced examples, for experienced users: " @echo "" @echo "make build ARCH=x86-64 COMP=clang" - @echo "make profile-build ARCH=x86-64-modern COMP=gcc COMPCXX=g++-4.8" + @echo "make profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-4.8" @echo "" From a83d1a0e800f494d3b4d0aa2ed6db5bdcda50fc3 Mon Sep 17 00:00:00 2001 From: 31m059 <37052095+31m059@users.noreply.github.com> Date: Fri, 13 Sep 2019 15:46:05 -0400 Subject: [PATCH 38/72] Use queens of either color in RookOnQueenFile The recently-added RookOnQueenFile evaluation term (36e4a86) provided a bonus for placing our rook on the same file as an enemy queen. Here, we relax a condition in this bonus, broadening its effect to any queen. It is also strategically desirable to place the rook on the same file as a friendly queen, so the restriction on the queen's color is removed. STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 66856 W: 14847 L: 14815 D: 37194 http://tests.stockfishchess.org/tests/view/5d7b3c6a0ebc5902d385bcf5 LTC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 86786 W: 14264 L: 14248 D: 58274 http://tests.stockfishchess.org/tests/view/5d7b4e9b0ebc5902d385c178 Closes https://github.com/official-stockfish/Stockfish/pull/2302 Bench: 3703909 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 73e2144c..ad76fc8d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -347,8 +347,8 @@ namespace { if (relative_rank(Us, s) >= RANK_5) score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]); - // Bonus for rook on same file as their queen - if (file_bb(s) & pos.pieces(Them, QUEEN)) + // Bonus for rook on the same file as a queen + if (file_bb(s) & pos.pieces(QUEEN)) score += RookOnQueenFile; // Bonus for rook on an open or semi-open file From e5cfa14f40a38d99d11a9c048c34858e3145fbcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sat, 14 Sep 2019 08:33:00 +0200 Subject: [PATCH 39/72] Assorted trivial cleanups No functional change --- AUTHORS | 1 + src/position.cpp | 4 ++-- src/search.cpp | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 207c5a85..e55f94fb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -60,6 +60,7 @@ Jacques B. (Timshel) Jan Ondruš (hxim) Jared Kish (Kurtbusch) Jarrod Torriero (DU-jdto) +Jean Gauthier (QuaisBla) Jean-Francois Romang (jromang) Jerry Donald Watson (jerrydonaldwatson) Jonathan Calovski (Mysseno) diff --git a/src/position.cpp b/src/position.cpp index db66f416..6336a5ed 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1305,8 +1305,8 @@ bool Position::pos_is_ok() const { continue; if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK) - || castlingRightsMask[castlingRookSquare[cr]] != (cr) - || (castlingRightsMask[square(c)] & (cr)) != (cr)) + || castlingRightsMask[castlingRookSquare[cr]] != cr + || (castlingRightsMask[square(c)] & cr) != cr) assert(0 && "pos_is_ok: Castling"); } diff --git a/src/search.cpp b/src/search.cpp index f8535a5f..79942bcd 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1050,7 +1050,7 @@ moves_loop: // When in check, search starts from here if (!pos.see_ge(move, Value(-(31 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) continue; } - else if ( (!givesCheck || !extension) + else if ( !(givesCheck && extension) && !pos.see_ge(move, Value(-199) * (depth / ONE_PLY))) // (~20 Elo) continue; } @@ -1096,7 +1096,7 @@ moves_loop: // When in check, search starts from here if ((ss-1)->moveCount > 15) r -= ONE_PLY; - // Decrease reduction if move has been singularly extended + // Decrease reduction if ttMove has been singularly extended r -= singularLMR * ONE_PLY; if (!captureOrPromotion) From 843a6c43053ceb9cc79d29bf7c0d3a5d236e057e Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sun, 15 Sep 2019 00:32:39 +0200 Subject: [PATCH 40/72] Introduce midgame initiative This patch finally introduces something that was tried for years: midgame score dependance on complexity of position. More precisely, if the position is very simplified and the complexity measure calculated in the initiative() function is inferior to -50 by an amount d, then we add this value d to the midgame score. One example of play of this patch will be (again!) 4 vs 3 etc same flank endgames where sides have a lot of non-pawn material: 4 vs 3 draw mostly remains the same draw even if we add a lot of equal material to both sides. STC run was stopped after 200k games (and not converging): LLR: -1.75 (-2.94,2.94) [0.50,4.50] Total: 200319 W: 44197 L: 43310 D: 112812 http://tests.stockfishchess.org/tests/view/5d7cfdb10ebc5902d386572c passed LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 41051 W: 6858 L: 6570 D: 27623 http://tests.stockfishchess.org/tests/view/5d7d14680ebc5902d3866196 This is the first and not really precise version, a lot of other stuff can be tried on top of it (separate complexity for middlegame, some more terms, even simple retuning of values). Bench: 4248476 --- src/evaluate.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ad76fc8d..d92239c1 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -169,7 +169,7 @@ namespace { template Score passed() const; template Score space() const; ScaleFactor scale_factor(Value eg) const; - Score initiative(Value eg) const; + Score initiative(Score score) const; const Position& pos; Material::Entry* me; @@ -717,7 +717,10 @@ namespace { // known attacking/defending status of the players. template - Score Evaluation::initiative(Value eg) const { + Score Evaluation::initiative(Score score) const { + + Value mg = mg_value(score); + Value eg = eg_value(score); int outflanking = distance(pos.square(WHITE), pos.square(BLACK)) - distance(pos.square(WHITE), pos.square(BLACK)); @@ -738,15 +741,16 @@ namespace { - 36 * almostUnwinnable -103 ; - // Now apply the bonus: note that we find the attacking side by extracting - // the sign of the endgame value, and that we carefully cap the bonus so - // that the endgame score will never change sign after the bonus. + // Now apply the bonus: note that we find the attacking side by extracting the + // sign of the midgame or endgame values, and that we carefully cap the bonus + // so that the midgame and endgame scores do not change sign after the bonus. + int u = ((mg > 0) - (mg < 0)) * std::max(std::min(complexity + 50, 0), -abs(mg)); int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg)); if (T) - Trace::add(INITIATIVE, make_score(0, v)); + Trace::add(INITIATIVE, make_score(u, v)); - return make_score(0, v); + return make_score(u, v); } @@ -822,7 +826,7 @@ namespace { + passed< WHITE>() - passed< BLACK>() + space< WHITE>() - space< BLACK>(); - score += initiative(eg_value(score)); + score += initiative(score); // Interpolate between a middlegame and a (scaled by 'sf') endgame score ScaleFactor sf = scale_factor(eg_value(score)); From 7b064752943b67eaa7ef93f1109acfad50b4ecc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 15 Sep 2019 23:18:54 +0200 Subject: [PATCH 41/72] Scale down endgame factor when shuffling This patch decreases the endgame scale factor using the 50 moves counter. Looking at some games with this patch, it seems to have two effects on the playing style: 1) when no progress can be made in late endgames (for instance in fortresses or opposite bishops endgames) the evaluation will be largely tamed down towards a draw value. 2) more interestingly, there is also a small effect in the midgame play because Stockfish will panic a little bit if there are more than four consecutive shuffling moves with an advantage: the engine will try to move a pawn or to exchange a piece to keep the advantage, so the follow-ups of the position will be discovered earlier by the alpha-beta search. passed STC: LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 23017 W: 5080 L: 4805 D: 13132 http://tests.stockfishchess.org/tests/view/5d7e4aef0ebc59069c36fc74 passed LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 30746 W: 5171 L: 4911 D: 20664 http://tests.stockfishchess.org/tests/view/5d7e513d0ebc59069c36ff26 Pull request: https://github.com/official-stockfish/Stockfish/pull/2304 Bench: 4272173 --- src/evaluate.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d92239c1..a7a091ab 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -769,8 +769,9 @@ namespace { && 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); + sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * pos.count(strongSide)); + sf = std::max(0, sf - (pos.rule50_count() - 12) / 4 ); } return ScaleFactor(sf); From a858defd332bddd828c9280a9e326a0b750b3dda Mon Sep 17 00:00:00 2001 From: noobpwnftw Date: Sun, 15 Sep 2019 00:18:10 +0800 Subject: [PATCH 42/72] Raise stack size to 8MB for pthreads It seems there is no other way to specify stack size on std::thread than linker flags and the effective flags are named differently in many toolchains. On toolchains where pthread is always available, this patch changes the stack size change in our C++ code via pthread to ensure a minimum stack size of 8MB, instead of relying on linker defaults which may be platform-specific. Also raises default stack size on OSX to current Linux default (8MB) just to be safe. Closes https://github.com/official-stockfish/Stockfish/pull/2303 No functional change --- src/thread_win32_osx.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index 88900540..5583a06e 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -73,11 +73,14 @@ typedef std::condition_variable ConditionVariable; /// adjust it to TH_STACK_SIZE. The implementation calls pthread_create() with /// proper stack size parameter. -#if defined(__APPLE__) +/// On toolchains where pthread is always available, ensure minimum stack size +/// instead of relying on linker defaults which may be platform-specific. + +#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) #include -static const size_t TH_STACK_SIZE = 2 * 1024 * 1024; +static const size_t TH_STACK_SIZE = 8 * 1024 * 1024; template > void* start_routine(void* ptr) From 64af5434ed2b775b054993de85aa015e5a120da3 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 22 Sep 2019 16:56:36 +0200 Subject: [PATCH 43/72] Acknowledge fishtest authors Explicitly acknowledge fishtest authors. Their efforts are almost invisible, but essential for the project. Many thanks to https://github.com/glinscott/fishtest/blob/master/AUTHORS ! No functional change. --- AUTHORS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AUTHORS b/AUTHORS index e55f94fb..455cf6ca 100644 --- a/AUTHORS +++ b/AUTHORS @@ -133,3 +133,7 @@ Tom Vijlbrief (tomtor) Torsten Franz (torfranz) Uri Blass (uriblass) Vince Negri + +# Additionally, we acknowledge the authors of fishtest, +# an essential framework for the development of Stockfish: +# https://github.com/glinscott/fishtest/blob/master/AUTHORS From 7e4c3256aab178f303578b4c4a31c59d43421640 Mon Sep 17 00:00:00 2001 From: protonspring Date: Sun, 22 Sep 2019 19:48:52 -0600 Subject: [PATCH 44/72] Simplify connected pawn scoring When scoring the connected pawns, replace the intricate ternary expressions choosing the coefficient by a simpler addition of boolean conditions: ` value = Connected * (2 + phalanx - opposed) ` This is the map showing the old coefficients and the new ones: ``` phalanx and unopposed: 3x -> 3x phalanx and opposed: 1.5x -> 2x not phalanx and unopposed: 2x -> 2x not phalanx and opposed: 1x -> 1x ``` STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 11354 W: 2579 L: 2437 D: 6338 http://tests.stockfishchess.org/tests/view/5d8151f00ebc5971531d244f LTC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 41221 W: 7001 L: 6913 D: 27307 http://tests.stockfishchess.org/tests/view/5d818f930ebc5971531d26d6 Bench: 3959889 blah --- src/evaluate.cpp | 2 +- src/pawns.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a7a091ab..9521cd10 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -353,7 +353,7 @@ namespace { // Bonus for rook on an open or semi-open file if (pos.is_on_semiopen_file(Us, s)) - score += RookOnFile[bool(pos.is_on_semiopen_file(Them, s))]; + score += RookOnFile[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/pawns.cpp b/src/pawns.cpp index 1ae65bf1..f6041199 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -130,7 +130,7 @@ namespace { // Score this pawn if (support | phalanx) { - int v = Connected[r] * (phalanx ? 3 : 2) / (opposed ? 2 : 1) + int v = Connected[r] * (2 + bool(phalanx) - opposed) + 17 * popcount(support); score += make_score(v, v * (r - 2) / 4); From 770c8d92f3886d11ae88afef13f6552f9132a714 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 20 Sep 2019 16:33:57 +0200 Subject: [PATCH 45/72] More random draw evaluations Use the randomized draw function value_draw() also for draw evalutions. This extends the earlier commit https://github.com/official-stockfish/Stockfish/commit/97d2cc9a9c1c4b6ff1b470676fa18c7fc6509886 which did this only for 3folds. As in that case, this test was yellow at STC and LTC, but green at VLTC, indicative of the fact that the higher the drawrate, the more likely this idea is beneficial. STC: LLR: -2.96 (-2.94,2.94) [0.50,4.50] Total: 83573 W: 18584 L: 18335 D: 46654 http://tests.stockfishchess.org/tests/view/5d84e44d0ebc5971531d4f94 LTC: LLR: -2.96 (-2.94,2.94) [0.00,3.50] Total: 92252 W: 15240 L: 15160 D: 61852 http://tests.stockfishchess.org/tests/view/5d865dd90ebc5971531d68e1 VLTC: 120+1.2 @ 2th LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 51902 W: 7323 L: 7028 D: 37551 http://tests.stockfishchess.org/tests/view/5d8763620ebc595f57c22b15 Closes https://github.com/official-stockfish/Stockfish/pull/2321 Bench: 3441237 --- src/search.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 79942bcd..2d5fb630 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -762,6 +762,9 @@ namespace { if (eval == VALUE_NONE) ss->staticEval = eval = evaluate(pos); + if (eval == VALUE_DRAW) + eval = value_draw(depth, thisThread); + // Can ttValue be used as a better position evaluation? if ( ttValue != VALUE_NONE && (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))) From 7756344d5d2b93970e7cd423f8cbf6fb1da11b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Mon, 23 Sep 2019 08:52:27 +0200 Subject: [PATCH 46/72] Clarify the mapping of files to queenside Author: @nickpelling We replace in the code the obscure expressions mapping files ABCDEFGH to ABCDDCBA by an explicite call to an auxiliary function : old: f = min(f, ~f) new: f = map_to_queenside(f) We used the Golbolt web site (https://godbolt.org) to find the optimal code for the auxiliary function. STC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 30292 W: 6756 L: 6651 D: 16885 http://tests.stockfishchess.org/tests/view/5d8676720ebc5971531d6aa1 No functional change --- src/evaluate.cpp | 2 +- src/pawns.cpp | 2 +- src/psqt.cpp | 2 +- src/types.h | 4 ++++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 9521cd10..f37820af 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -662,7 +662,7 @@ namespace { || (pos.pieces(PAWN) & (s + Up))) bonus = bonus / 2; - score += bonus - PassedFile * std::min(f, ~f); + score += bonus - PassedFile * map_to_queenside(f); } if (T) diff --git a/src/pawns.cpp b/src/pawns.cpp index f6041199..d822ef4e 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -198,7 +198,7 @@ Score Entry::evaluate_shelter(const Position& pos, Square ksq) { b = theirPawns & file_bb(f); int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; - int d = std::min(f, ~f); + int d = map_to_queenside(f); bonus += make_score(ShelterStrength[d][ourRank], 0); if (ourRank && (ourRank == theirRank - 1)) diff --git a/src/psqt.cpp b/src/psqt.cpp index 36d99feb..13e69499 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -119,7 +119,7 @@ void init() { for (Square s = SQ_A1; s <= SQ_H8; ++s) { - File f = std::min(file_of(s), ~file_of(s)); + File f = map_to_queenside(file_of(s)); psq[ pc][ s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)] : Bonus[pc][rank_of(s)][f]); psq[~pc][~s] = -psq[pc][s]; diff --git a/src/types.h b/src/types.h index c77d8040..6af03687 100644 --- a/src/types.h +++ b/src/types.h @@ -366,6 +366,10 @@ constexpr Piece operator~(Piece pc) { return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT } +inline File map_to_queenside(File f) { + return std::min(f, File(FILE_H - f)); // Map files ABCDEFGH to files ABCDDCBA +} + constexpr CastlingRights operator&(Color c, CastlingRights cr) { return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr); } From 302e0f70c653bfcca4338d7be71dfdd1166da910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Mon, 23 Sep 2019 09:10:28 +0200 Subject: [PATCH 47/72] Revert "Clarify the mapping of files to queenside" This reverts commit 7756344d5d2b93970e7cd423f8cbf6fb1da11b74. --- src/evaluate.cpp | 2 +- src/pawns.cpp | 2 +- src/psqt.cpp | 2 +- src/types.h | 4 ---- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index f37820af..9521cd10 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -662,7 +662,7 @@ namespace { || (pos.pieces(PAWN) & (s + Up))) bonus = bonus / 2; - score += bonus - PassedFile * map_to_queenside(f); + score += bonus - PassedFile * std::min(f, ~f); } if (T) diff --git a/src/pawns.cpp b/src/pawns.cpp index d822ef4e..f6041199 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -198,7 +198,7 @@ Score Entry::evaluate_shelter(const Position& pos, Square ksq) { b = theirPawns & file_bb(f); int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; - int d = map_to_queenside(f); + int d = std::min(f, ~f); bonus += make_score(ShelterStrength[d][ourRank], 0); if (ourRank && (ourRank == theirRank - 1)) diff --git a/src/psqt.cpp b/src/psqt.cpp index 13e69499..36d99feb 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -119,7 +119,7 @@ void init() { for (Square s = SQ_A1; s <= SQ_H8; ++s) { - File f = map_to_queenside(file_of(s)); + File f = std::min(file_of(s), ~file_of(s)); psq[ pc][ s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)] : Bonus[pc][rank_of(s)][f]); psq[~pc][~s] = -psq[pc][s]; diff --git a/src/types.h b/src/types.h index 6af03687..c77d8040 100644 --- a/src/types.h +++ b/src/types.h @@ -366,10 +366,6 @@ constexpr Piece operator~(Piece pc) { return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT } -inline File map_to_queenside(File f) { - return std::min(f, File(FILE_H - f)); // Map files ABCDEFGH to files ABCDDCBA -} - constexpr CastlingRights operator&(Color c, CastlingRights cr) { return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr); } From defa1ccaa9c145b0ccff38a1ae660c052d753e81 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Mon, 23 Sep 2019 08:24:13 +0100 Subject: [PATCH 48/72] Encourage rook lift to third rank This change to the Rook psqt encourages rook lifts to the third rank on the two center files. STC 10+0.1 th 1 : LLR: 2.96 (-2.94,2.94) [0.00,4.00] Total: 40654 W: 9028 L: 8704 D: 22922 http://tests.stockfishchess.org/tests/view/5d885da60ebc5906dd3e9fcd LTC 60+0.6 th 1 : LLR: 2.96 (-2.94,2.94) [0.00,4.00] Total: 56963 W: 9530 L: 9196 D: 38237 http://tests.stockfishchess.org/tests/view/5d88618c0ebc5906dd3ea45f Thanks to @snicolet for mentioning that Komodo does this a lot and Stockfish doesn't, which gave me the idea for this patch, and to @noobpwnftw for providing cores to fishtest which allowed very quick testing. Future work: perhaps this can be refined somehow to encourage this on other files, my attempts have failed. Closes https://github.com/official-stockfish/Stockfish/pull/2322 Bench: 3950249 --- src/psqt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psqt.cpp b/src/psqt.cpp index 36d99feb..2b06931c 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -61,7 +61,7 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { { // Rook { S(-24, -2), S(-13,-6), S(-7, -3), S( 2,-2) }, { S(-18,-10), S(-10,-7), S(-5, 1), S( 9, 0) }, - { S(-21, 10), S( -7,-4), S( 3, 2), S(-1,-2) }, + { S(-21, 10), S( -7,-4), S( 3, 2), S( 7,-2) }, { S(-13, -5), S( -5, 2), S(-4, -8), S(-6, 8) }, { S(-24, -8), S(-12, 5), S(-1, 4), S( 6,-9) }, { S(-24, 3), S( -4,-2), S( 4,-10), S(10, 7) }, From d232a4ae684eccd829fd703a1872c1e0e17aaee9 Mon Sep 17 00:00:00 2001 From: nickpelling Date: Sat, 21 Sep 2019 08:59:32 +0100 Subject: [PATCH 49/72] Clarify the mapping of files to queenside This patch replaces the obscure expressions mapping files ABCDEFGH to ABCDDCBA by explicite calls to an auxiliary function: old: f = min(f, ~f) new: f = map_to_queenside(f) We used the Golbolt web site (https://godbolt.org) to check that the current code for the auxiliary function is optimal. STC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 30292 W: 6756 L: 6651 D: 16885 http://tests.stockfishchess.org/tests/view/5d8676720ebc5971531d6aa1 Achieved with a bit of help from Sopel97, snicolet and vondele, thanks everyone! Closes https://github.com/official-stockfish/Stockfish/pull/2325 No functional change --- AUTHORS | 1 + src/evaluate.cpp | 2 +- src/pawns.cpp | 2 +- src/psqt.cpp | 2 +- src/types.h | 9 +++++---- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/AUTHORS b/AUTHORS index 455cf6ca..aff6a6c9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -99,6 +99,7 @@ Miroslav Fontán (Hexik) Moez Jellouli (MJZ1977) Mohammed Li (tthsqe12) Nathan Rugg (nmrugg) +Nick Pelling (nickpelling) Nicklas Persson (NicklasPersson) Niklas Fiekas (niklasf) Ondrej Mosnáček (WOnder93) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 9521cd10..f37820af 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -662,7 +662,7 @@ namespace { || (pos.pieces(PAWN) & (s + Up))) bonus = bonus / 2; - score += bonus - PassedFile * std::min(f, ~f); + score += bonus - PassedFile * map_to_queenside(f); } if (T) diff --git a/src/pawns.cpp b/src/pawns.cpp index f6041199..ca4156da 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -198,7 +198,7 @@ Score Entry::evaluate_shelter(const Position& pos, Square ksq) { b = theirPawns & file_bb(f); int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; - int d = std::min(f, ~f); + File d = map_to_queenside(f); bonus += make_score(ShelterStrength[d][ourRank], 0); if (ourRank && (ourRank == theirRank - 1)) diff --git a/src/psqt.cpp b/src/psqt.cpp index 2b06931c..83d11cf1 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -119,7 +119,7 @@ void init() { for (Square s = SQ_A1; s <= SQ_H8; ++s) { - File f = std::min(file_of(s), ~file_of(s)); + File f = map_to_queenside(file_of(s)); psq[ pc][ s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)] : Bonus[pc][rank_of(s)][f]); psq[~pc][~s] = -psq[pc][s]; diff --git a/src/types.h b/src/types.h index c77d8040..6d2c09a6 100644 --- a/src/types.h +++ b/src/types.h @@ -43,6 +43,7 @@ #include #include #include +#include #if defined(_MSC_VER) // Disable some silly and noisy warning from MSVC compiler @@ -358,14 +359,14 @@ constexpr Square operator~(Square s) { return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8 } -constexpr File operator~(File f) { - return File(f ^ FILE_H); // Horizontal flip FILE_A -> FILE_H -} - constexpr Piece operator~(Piece pc) { return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT } +inline File map_to_queenside(File f) { + return std::min(f, File(FILE_H - f)); // Map files ABCDEFGH to files ABCDDCBA +} + constexpr CastlingRights operator&(Color c, CastlingRights cr) { return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr); } From 667d24f22743959ceddda6af53718619ea5c551d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Tue, 24 Sep 2019 12:41:45 +0200 Subject: [PATCH 50/72] Increase weight for supported pawns This patch changes the weight for counting supports of pawns from 17 to 21. Hopefully Stockfish will accept to play a bit more of closed or semi-closed positions. STC: LLR: 2.95 (-2.94,2.94) [0.00,4.00] Total: 13822 W: 3158 L: 2939 D: 7725 http://tests.stockfishchess.org/tests/view/5d89c3a10ebc595091802379 LTC: LLR: 2.96 (-2.94,2.94) [0.00,4.00] Total: 63066 W: 10590 L: 10236 D: 42240 http://tests.stockfishchess.org/tests/view/5d89ca7f0ebc595091802680 Future work: try to tweak the evaluation to better understand the French structures. Closes https://github.com/official-stockfish/Stockfish/pull/2326 Bench: 3618154 --- src/pawns.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index ca4156da..50eb3aa3 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -131,7 +131,7 @@ namespace { if (support | phalanx) { int v = Connected[r] * (2 + bool(phalanx) - opposed) - + 17 * popcount(support); + + 21 * popcount(support); score += make_score(v, v * (r - 2) / 4); } From 0436f01d05f05e2669cbb5b4bee752145b5b9991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Tue, 24 Sep 2019 19:00:27 +0200 Subject: [PATCH 51/72] Temporary patch to show the compiler for TCEC submission This patch shows a description of the compiler used to compile Stockfish, when starting from the console. Usage: ``` ./stockfish compiler ``` Example of output: ``` Stockfish 240919 64 POPCNT by T. Romstad, M. Costalba, J. Kiiski, G. Linscott Compiled by clang++ 9.0.0 on Apple __VERSION__ macro expands to: 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.38) ``` No functional change --- src/misc.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/misc.h | 1 + src/uci.cpp | 9 ++++--- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index b1539ce2..a5717e7a 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -144,6 +144,77 @@ const string engine_info(bool to_uci) { } +/// compiler_info() returns a string trying to describe the compiler we use + +const std::string compiler_info() { + + #define STRINGIFY2(x) #x + #define STRINGIFY(x) STRINGIFY2(x) + #define VER_STRING(major, minor, patch) STRINGIFY(major) "." STRINGIFY(minor) "." STRINGIFY(patch) + +/// Predefined macros hell: +/// +/// __GNUC__ Compiler is gcc, Clang or Intel on Linux +/// __INTEL_COMPILER Compiler is Intel +/// _MSC_VER Compiler is MSVC or Intel on Windows +/// _WIN32 Building on Windows (any) +/// _WIN64 Building on Windows 64 bit + + std::string compiler = "\nCompiled by "; + + #ifdef __clang__ + compiler += "clang++ "; + compiler += VER_STRING(__clang_major__, __clang_minor__, __clang_patchlevel__); + #elif __INTEL_COMPILER + compiler += "Intel compiler "; + compiler += "(version "; + compiler += STRINGIFY(__INTEL_COMPILER) " update " STRINGIFY(__INTEL_COMPILER_UPDATE); + compiler += ")"; + #elif _MSC_VER + compiler += "MSVC "; + compiler += "(version "; + compiler += STRINGIFY(_MSC_FULL_VER) "." STRINGIFY(_MSC_BUILD); + compiler += ")"; + #elif __GNUC__ + compiler += "g++ (GNUC) "; + compiler += VER_STRING(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); + #else + compiler += "Unknown compiler "; + compiler += "(unknown version)"; + #endif + + #if defined(__APPLE__) + compiler += " on Apple"; + #elif defined(__CYGWIN__) + compiler += " on Cygwin"; + #elif defined(__MINGW64__) + compiler += " on MinGW64"; + #elif defined(__MINGW32__) + compiler += " on MinGW32"; + #elif defined(__ANDROID__) + compiler += " on Android"; + #elif defined(__linux__) + compiler += " on Linux"; + #elif defined(_WIN64) + compiler += " on Microsoft Windows 64-bit"; + #elif defined(_WIN32) + compiler += " on Microsoft Windows 32-bit"; + #else + compiler += " on unknown system"; + #endif + + compiler += "\n __VERSION__ macro expands to: "; + #ifdef __VERSION__ + compiler += __VERSION__; + #else + compiler += "(undefined macro)"; + #endif + compiler += "\n"; + + return compiler; +} + + /// Debug functions used mainly to collect run-time statistics static std::atomic hits[2], means[2]; diff --git a/src/misc.h b/src/misc.h index ddd05e4e..0efdd562 100644 --- a/src/misc.h +++ b/src/misc.h @@ -30,6 +30,7 @@ #include "types.h" const std::string engine_info(bool to_uci = false); +const std::string compiler_info(); void prefetch(void* addr); void start_logger(const std::string& fname); diff --git a/src/uci.cpp b/src/uci.cpp index a4235f2b..6e560572 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -230,10 +230,11 @@ void UCI::loop(int argc, char* argv[]) { else if (token == "isready") sync_cout << "readyok" << sync_endl; // Additional custom non-UCI commands, mainly for debugging - else if (token == "flip") pos.flip(); - else if (token == "bench") bench(pos, is, states); - else if (token == "d") sync_cout << pos << sync_endl; - else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; + else if (token == "flip") pos.flip(); + else if (token == "bench") bench(pos, is, states); + else if (token == "d") sync_cout << pos << sync_endl; + else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; + else if (token == "compiler") sync_cout << compiler_info() << sync_endl; else sync_cout << "Unknown command: " << cmd << sync_endl; From 8726beba59ca10d03cfee5073e4ac51da7cae42a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 26 Sep 2019 23:19:31 +0200 Subject: [PATCH 52/72] Restore development version (revert previous commit) Revert the previous patch now that the binary for the super-final of TCEC season 16 has been sent. Maybe the feature of showing the name of compiler will be added to the master branch in the future. But we may use a cleaner way to code it, see some ideas using the Makefile approach at the end of pull request #2327 : https://github.com/official-stockfish/Stockfish/pull/2327 Bench: 3618154 --- src/misc.cpp | 71 ---------------------------------------------------- src/misc.h | 1 - src/uci.cpp | 9 +++---- 3 files changed, 4 insertions(+), 77 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index a5717e7a..b1539ce2 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -144,77 +144,6 @@ const string engine_info(bool to_uci) { } -/// compiler_info() returns a string trying to describe the compiler we use - -const std::string compiler_info() { - - #define STRINGIFY2(x) #x - #define STRINGIFY(x) STRINGIFY2(x) - #define VER_STRING(major, minor, patch) STRINGIFY(major) "." STRINGIFY(minor) "." STRINGIFY(patch) - -/// Predefined macros hell: -/// -/// __GNUC__ Compiler is gcc, Clang or Intel on Linux -/// __INTEL_COMPILER Compiler is Intel -/// _MSC_VER Compiler is MSVC or Intel on Windows -/// _WIN32 Building on Windows (any) -/// _WIN64 Building on Windows 64 bit - - std::string compiler = "\nCompiled by "; - - #ifdef __clang__ - compiler += "clang++ "; - compiler += VER_STRING(__clang_major__, __clang_minor__, __clang_patchlevel__); - #elif __INTEL_COMPILER - compiler += "Intel compiler "; - compiler += "(version "; - compiler += STRINGIFY(__INTEL_COMPILER) " update " STRINGIFY(__INTEL_COMPILER_UPDATE); - compiler += ")"; - #elif _MSC_VER - compiler += "MSVC "; - compiler += "(version "; - compiler += STRINGIFY(_MSC_FULL_VER) "." STRINGIFY(_MSC_BUILD); - compiler += ")"; - #elif __GNUC__ - compiler += "g++ (GNUC) "; - compiler += VER_STRING(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); - #else - compiler += "Unknown compiler "; - compiler += "(unknown version)"; - #endif - - #if defined(__APPLE__) - compiler += " on Apple"; - #elif defined(__CYGWIN__) - compiler += " on Cygwin"; - #elif defined(__MINGW64__) - compiler += " on MinGW64"; - #elif defined(__MINGW32__) - compiler += " on MinGW32"; - #elif defined(__ANDROID__) - compiler += " on Android"; - #elif defined(__linux__) - compiler += " on Linux"; - #elif defined(_WIN64) - compiler += " on Microsoft Windows 64-bit"; - #elif defined(_WIN32) - compiler += " on Microsoft Windows 32-bit"; - #else - compiler += " on unknown system"; - #endif - - compiler += "\n __VERSION__ macro expands to: "; - #ifdef __VERSION__ - compiler += __VERSION__; - #else - compiler += "(undefined macro)"; - #endif - compiler += "\n"; - - return compiler; -} - - /// Debug functions used mainly to collect run-time statistics static std::atomic hits[2], means[2]; diff --git a/src/misc.h b/src/misc.h index 0efdd562..ddd05e4e 100644 --- a/src/misc.h +++ b/src/misc.h @@ -30,7 +30,6 @@ #include "types.h" const std::string engine_info(bool to_uci = false); -const std::string compiler_info(); void prefetch(void* addr); void start_logger(const std::string& fname); diff --git a/src/uci.cpp b/src/uci.cpp index 6e560572..a4235f2b 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -230,11 +230,10 @@ void UCI::loop(int argc, char* argv[]) { else if (token == "isready") sync_cout << "readyok" << sync_endl; // Additional custom non-UCI commands, mainly for debugging - else if (token == "flip") pos.flip(); - else if (token == "bench") bench(pos, is, states); - else if (token == "d") sync_cout << pos << sync_endl; - else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; - else if (token == "compiler") sync_cout << compiler_info() << sync_endl; + else if (token == "flip") pos.flip(); + else if (token == "bench") bench(pos, is, states); + else if (token == "d") sync_cout << pos << sync_endl; + else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; else sync_cout << "Unknown command: " << cmd << sync_endl; From d703d2b5e760edf4d34b6664f2d0552ffe424b12 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 16 Sep 2019 07:51:25 +0200 Subject: [PATCH 53/72] Remove custom mutex implementation As part of the investigation of the hang caused by an incorrect implementation of condition_variable in libwinpthread, it was realized that our custom Mutex implementation is no longer needed. Prior to lazySMP this custom implementation resulted in a 30% speedup, but now no speed difference can be measured as no mutex is used on the hot path in lazySMP. https://github.com/official-stockfish/Stockfish/issues/2291 https://github.com/official-stockfish/Stockfish/issues/2309#issuecomment-533733393 https://github.com/official-stockfish/Stockfish/issues/2309#issuecomment-533737515 The interest of this patch is that it removes platform-specific code, which is always less tested. No functional change. --- src/misc.cpp | 2 +- src/syzygy/tbprobe.cpp | 10 +++++--- src/thread.cpp | 6 ++--- src/thread.h | 4 +-- src/thread_win32_osx.h | 55 +++--------------------------------------- 5 files changed, 16 insertions(+), 61 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index b1539ce2..17644eed 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -168,7 +168,7 @@ void dbg_print() { std::ostream& operator<<(std::ostream& os, SyncCout sc) { - static Mutex m; + static std::mutex m; if (sc == IO_LOCK) m.lock(); diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 10864744..c7d20788 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -27,12 +27,12 @@ #include #include #include +#include #include "../bitboard.h" #include "../movegen.h" #include "../position.h" #include "../search.h" -#include "../thread_win32_osx.h" #include "../types.h" #include "../uci.h" @@ -45,7 +45,9 @@ #include #else #define WIN32_LEAN_AND_MEAN -#define NOMINMAX +#ifndef NOMINMAX +# define NOMINMAX // Disable macros min() and max() +#endif #include #endif @@ -1124,14 +1126,14 @@ void set(T& e, uint8_t* data) { template void* mapped(TBTable& e, const Position& pos) { - static Mutex mutex; + static std::mutex mutex; // Use 'acquire' to avoid a thread reading 'ready' == true while // another is still working. (compiler reordering may cause this). if (e.ready.load(std::memory_order_acquire)) return e.baseAddress; // Could be nullptr if file does not exist - std::unique_lock lk(mutex); + std::unique_lock lk(mutex); if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock return e.baseAddress; diff --git a/src/thread.cpp b/src/thread.cpp index 5165fd90..f7445f98 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -81,7 +81,7 @@ void Thread::clear() { void Thread::start_searching() { - std::lock_guard lk(mutex); + std::lock_guard lk(mutex); searching = true; cv.notify_one(); // Wake up the thread in idle_loop() } @@ -92,7 +92,7 @@ void Thread::start_searching() { void Thread::wait_for_search_finished() { - std::unique_lock lk(mutex); + std::unique_lock lk(mutex); cv.wait(lk, [&]{ return !searching; }); } @@ -112,7 +112,7 @@ void Thread::idle_loop() { while (true) { - std::unique_lock lk(mutex); + std::unique_lock lk(mutex); searching = false; cv.notify_one(); // Wake up anyone waiting for search finished cv.wait(lk, [&]{ return searching; }); diff --git a/src/thread.h b/src/thread.h index ed427b10..79c6e43f 100644 --- a/src/thread.h +++ b/src/thread.h @@ -42,8 +42,8 @@ class Thread { - Mutex mutex; - ConditionVariable cv; + std::mutex mutex; + std::condition_variable cv; size_t idx; bool exit = false, searching = true; // Set before starting std::thread NativeThread stdThread; diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index 5583a06e..f8cb466b 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -21,60 +21,13 @@ #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 -/// on top of Windows semaphores. Semaphores, being kernel objects, require kernel -/// mode transition in order to lock or unlock, which is very slow compared to -/// interlocked operations (about 30% slower on bench test). To work around this -/// issue, we define our wrappers to the low level Win32 calls. We use critical -/// sections to support Windows XP and older versions. Unfortunately, cond_wait() -/// is racy between unlock() and WaitForSingleObject() but they have the same -/// speed performance as the SRW locks. - -#include -#include #include -#if defined(_WIN32) && !defined(_MSC_VER) - -#ifndef NOMINMAX -# define NOMINMAX // Disable macros min() and max() -#endif - -#define WIN32_LEAN_AND_MEAN -#include -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX - -/// Mutex and ConditionVariable struct are wrappers of the low level locking -/// machinery and are modeled after the corresponding C++11 classes. - -struct Mutex { - Mutex() { InitializeCriticalSection(&cs); } - ~Mutex() { DeleteCriticalSection(&cs); } - void lock() { EnterCriticalSection(&cs); } - void unlock() { LeaveCriticalSection(&cs); } - -private: - CRITICAL_SECTION cs; -}; - -typedef std::condition_variable_any ConditionVariable; - -#else // Default case: use STL classes - -typedef std::mutex Mutex; -typedef std::condition_variable ConditionVariable; - -#endif - /// 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. - -/// On toolchains where pthread is always available, ensure minimum stack size -/// instead of relying on linker defaults which may be platform-specific. +/// size of 512KB by default, this is too low for deep searches, which require +/// somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE. +/// The implementation calls pthread_create() with the stack size parameter +/// equal to the linux 8MB default, on platforms that support it. #if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) From 28dcd700a9dd3c776b66b2032cf0cf6df88b671d Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Wed, 25 Sep 2019 23:23:07 -0400 Subject: [PATCH 54/72] Simplify RookOnPawn Remove the RookOnPawn logic (for rook on rank 5 and above aligning with pawns on same row or file) which was overlapping with a few other parameters. Inspired by @31m059 interesting result hinting that a direct attack on pawns instead of PseudoAttacks might work. http://tests.stockfishchess.org/tests/view/5d89a7c70ebc595091801b8d After a few attempts by me and @31m059, and some long STC greens but red LTC, as a proof of concept I first tried a local SPSA at VSTC trying to tune related rook psqt rows, and mainly some rook related stuff in evaluate.cpp. Result was STC green, but still red LTC, Finally a 100M fishtest SPSA at LTC proved successful both at STC and LTC. All this was possible with the awesome fishtest contributors. At some point, I had 850 workers on the last test ! Run as a simplification STC http://tests.stockfishchess.org/tests/view/5d8d68f40ebc590f3beaf171 LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 7399 W: 1693 L: 1543 D: 4163 LTC http://tests.stockfishchess.org/tests/view/5d8d70270ebc590f3beaf63c LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 41617 W: 6981 L: 6894 D: 27742 Closes https://github.com/official-stockfish/Stockfish/pull/2329 bench: 4037914 --- src/evaluate.cpp | 15 +++++---------- src/psqt.cpp | 16 ++++++++-------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index f37820af..880aa137 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -78,7 +78,7 @@ namespace { constexpr Value SpaceThreshold = Value(12222); // KingAttackWeights[PieceType] contains king attack weights by piece type - constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 77, 55, 44, 10 }; + constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; // Penalties for enemy's safe checks constexpr int QueenSafeCheck = 780; @@ -108,17 +108,17 @@ namespace { // RookOnFile[semiopen/open] contains bonuses for each rook when there is // no (friendly) pawn on the rook file. - constexpr Score RookOnFile[] = { S(18, 7), S(44, 20) }; + constexpr Score RookOnFile[] = { S(21, 4), S(47, 25) }; // ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to // which piece type attacks which one. Attacks on lesser pieces which are // pawn-defended are not considered. constexpr Score ThreatByMinor[PIECE_TYPE_NB] = { - S(0, 0), S(0, 31), S(39, 42), S(57, 44), S(68, 112), S(62, 120) + S(0, 0), S(6, 28), S(39, 42), S(57, 44), S(68, 112), S(62, 120) }; constexpr Score ThreatByRook[PIECE_TYPE_NB] = { - S(0, 0), S(0, 24), S(38, 71), S(38, 61), S(0, 38), S(51, 38) + S(0, 0), S(3, 44), S(38, 71), S(38, 61), S(0, 38), S(51, 38) }; // PassedRank[Rank] contains a bonus according to the rank of a passed pawn @@ -139,8 +139,7 @@ namespace { constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); constexpr Score RestrictedPiece = S( 7, 7); - constexpr Score RookOnPawn = S( 10, 32); - constexpr Score RookOnQueenFile = S( 11, 4); + constexpr Score RookOnQueenFile = S( 7, 6); constexpr Score SliderOnQueen = S( 59, 18); constexpr Score ThreatByKing = S( 24, 89); constexpr Score ThreatByPawnPush = S( 48, 39); @@ -343,10 +342,6 @@ namespace { if (Pt == ROOK) { - // Bonus for aligning rook with enemy pawns on the same rank/file - if (relative_rank(Us, s) >= RANK_5) - score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]); - // Bonus for rook on the same file as a queen if (file_bb(s) & pos.pieces(QUEEN)) score += RookOnQueenFile; diff --git a/src/psqt.cpp b/src/psqt.cpp index 83d11cf1..655eb993 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -59,14 +59,14 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { { S(-48,-51), S( -3,-40), S(-12,-39), S(-25,-20) } }, { // Rook - { S(-24, -2), S(-13,-6), S(-7, -3), S( 2,-2) }, - { S(-18,-10), S(-10,-7), S(-5, 1), S( 9, 0) }, - { S(-21, 10), S( -7,-4), S( 3, 2), S( 7,-2) }, - { S(-13, -5), S( -5, 2), S(-4, -8), S(-6, 8) }, - { S(-24, -8), S(-12, 5), S(-1, 4), S( 6,-9) }, - { S(-24, 3), S( -4,-2), S( 4,-10), S(10, 7) }, - { S( -8, 1), S( 6, 2), S(10, 17), S(12,-8) }, - { S(-22, 12), S(-24,-6), S(-6, 13), S( 4, 7) } + { S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) }, + { S(-21,-12), S(-13, -9), S( -8, -1), S( 6, -2) }, + { S(-25, 6), S(-11, -8), S( -1, -2), S( 3, -6) }, + { S(-13, -6), S( -5, 1), S( -4, -9), S(-6, 7) }, + { S(-27, -5), S(-15, 8), S( -4, 7), S( 3, -6) }, + { S(-22, 6), S( -2, 1), S( 6, -7), S(12, 10) }, + { S( -2, 4), S( 12, 5), S( 16, 20), S(18, -5) }, + { S(-17, 18), S(-19, 0), S( -1, 19), S( 9, 13) } }, { // Queen { S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) }, From 3a3ca6af0390d74427c218f29cb5fe1a913efb42 Mon Sep 17 00:00:00 2001 From: 31m059 <37052095+31m059@users.noreply.github.com> Date: Fri, 27 Sep 2019 02:45:28 -0400 Subject: [PATCH 55/72] Extend castling independently of singular extension A curious feature of Stockfish's current extension code is its repeated use of "else if." In most cases, this makes no functional difference, because no more than one extension is applied; once one extension has been applied, the remaining ones can be safely ignored. However, if most singular extension search conditions are true, except "value < singularBeta", no non-singular extensions (e.g., castling) can be performed! Three tests were submitted, for three of Stockfish's four non-singular extensions. I excluded the shuffle extension, because historically there have been concerns about the fragility of its conditions, and I did not want to risk causing any serious search problems. - Modifying the passed pawn extension appeared roughly neutral at STC. At best, it appeared to be an improvement of less than 1 Elo. - Modifying check extension performed very poorly at STC - Modifying castling extension (this patch) produced a long "yellow" run at STC (insufficient to pass, but positive score) and a strong LTC. In simple terms, prior to this patch castling extension was occasionally not applied during search--on castling moves. The effect of this patch is to perform castling extension on more castling moves. It does so without adding any code complexity, simply by replacing an "else if" with "if" and reordering some existing code. STC: LLR: -2.96 (-2.94,2.94) [0.00,4.00] Total: 108114 W: 23877 L: 23615 D: 60622 http://tests.stockfishchess.org/tests/view/5d8d86bd0ebc590f3beb0c88 LTC: LLR: 2.96 (-2.94,2.94) [0.00,4.00] Total: 20862 W: 3517 L: 3298 D: 14047 http://tests.stockfishchess.org/tests/view/5d8d99cd0ebc590f3beb1899 Bench: 3728191 -------- Where do we go from here? - It seems strange to me that check extension performed so poorly -- clearly some of the singular extension conditions are also very important for check extension. I am not an expert in search, and I do not have any intuition about which of the eight conditions is/are the culprit. I will try a succession of eight STC tests to identify the relevant conditions, then try to replicate this PR for check extension. - Recent tests interacting with the castle extension may deserve retesting. I will shortly resubmit a few of my recent castling extension tweaks, rebased on this PR/commit. My deepest thanks to @noobpwnftw for the extraordinary CPU donation, and to all our other fishtest volunteers, who made it possible for a speculative LTC to pass in 70 minutes! Closes https://github.com/official-stockfish/Stockfish/pull/2331 --- src/search.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2d5fb630..01de12a4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -997,10 +997,6 @@ moves_loop: // When in check, search starts from here && (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move))) extension = ONE_PLY; - // Castling extension - else if (type_of(move) == CASTLING) - extension = ONE_PLY; - // Shuffle extension else if ( PvNode && pos.rule50_count() > 18 @@ -1013,6 +1009,10 @@ moves_loop: // When in check, search starts from here && pos.advanced_pawn_push(move) && pos.pawn_passed(us, to_sq(move))) extension = ONE_PLY; + + // Castling extension + if (type_of(move) == CASTLING) + extension = ONE_PLY; // Calculate new depth for this move newDepth = depth - ONE_PLY + extension; From 70a38d726410dae06949e9cfd6be2fd58743101a Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 27 Sep 2019 19:25:22 +0200 Subject: [PATCH 56/72] Remove depth dependence in value_draw(). The condition "depth >= 4 * ONE_PLY" does not seem needed at this point. passed STC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 32751 W: 7178 L: 7078 D: 18495 http://tests.stockfishchess.org/tests/view/5d8e46660ebc590f3bebad5e passed LTC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 31693 W: 5299 L: 5196 D: 21198 http://tests.stockfishchess.org/tests/view/5d8e4b4f0ebc590f3bebb165 Bench: 4062526 --- src/search.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 01de12a4..2ad535cc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -86,9 +86,8 @@ namespace { } // Add a small random component to draw evaluations to avoid 3fold-blindness - Value value_draw(Depth depth, Thread* thisThread) { - return depth < 4 * ONE_PLY ? VALUE_DRAW - : VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1); + Value value_draw(Thread* thisThread) { + return VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1); } // Skill structure is used to implement strength limit @@ -574,7 +573,7 @@ namespace { && !rootNode && pos.has_game_cycle(ss->ply)) { - alpha = value_draw(depth, pos.this_thread()); + alpha = value_draw(pos.this_thread()); if (alpha >= beta) return alpha; } @@ -624,7 +623,7 @@ namespace { || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return (ss->ply >= MAX_PLY && !inCheck) ? evaluate(pos) - : value_draw(depth, pos.this_thread()); + : value_draw(pos.this_thread()); // Step 3. Mate distance pruning. Even if we mate at the next move our score // would be at best mate_in(ss->ply+1), but if alpha is already bigger because @@ -763,7 +762,7 @@ namespace { ss->staticEval = eval = evaluate(pos); if (eval == VALUE_DRAW) - eval = value_draw(depth, thisThread); + eval = value_draw(thisThread); // Can ttValue be used as a better position evaluation? if ( ttValue != VALUE_NONE From abd4400c874ab178d04c08d3668f3843aece114e Mon Sep 17 00:00:00 2001 From: protonspring Date: Mon, 30 Sep 2019 15:10:44 -0600 Subject: [PATCH 57/72] Remove ThreatByRank This is a functional simplification that removes ThreatByRank. STC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 48009 W: 10630 L: 10560 D: 26819 http://tests.stockfishchess.org/tests/view/5d92095c0ebc594fb88eb61e LTC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 18682 W: 3177 L: 3053 D: 12452 http://tests.stockfishchess.org/tests/view/5d9231120ebc594fb88ebacd Moving forward, it's possible that ThreatByMinor and ThreatByRook could be combined, but I haven't really contemplated that yet. Closes https://github.com/official-stockfish/Stockfish/pull/2336 bench 4088701 --- src/evaluate.cpp | 17 +++-------------- src/search.cpp | 2 +- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 880aa137..fadc0fea 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -114,7 +114,7 @@ namespace { // which piece type attacks which one. Attacks on lesser pieces which are // pawn-defended are not considered. constexpr Score ThreatByMinor[PIECE_TYPE_NB] = { - S(0, 0), S(6, 28), S(39, 42), S(57, 44), S(68, 112), S(62, 120) + S(0, 0), S(6, 32), S(59, 41), S(79, 56), S(90, 119), S(79, 161) }; constexpr Score ThreatByRook[PIECE_TYPE_NB] = { @@ -143,7 +143,6 @@ namespace { constexpr Score SliderOnQueen = S( 59, 18); constexpr Score ThreatByKing = S( 24, 89); constexpr Score ThreatByPawnPush = S( 48, 39); - constexpr Score ThreatByRank = S( 13, 0); constexpr Score ThreatBySafePawn = S(173, 94); constexpr Score TrappedRook = S( 47, 4); constexpr Score WeakQueen = S( 49, 15); @@ -510,21 +509,11 @@ namespace { { b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]); while (b) - { - Square s = pop_lsb(&b); - score += ThreatByMinor[type_of(pos.piece_on(s))]; - if (type_of(pos.piece_on(s)) != PAWN) - score += ThreatByRank * (int)relative_rank(Them, s); - } + score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(&b)))]; b = weak & attackedBy[Us][ROOK]; while (b) - { - Square s = pop_lsb(&b); - score += ThreatByRook[type_of(pos.piece_on(s))]; - if (type_of(pos.piece_on(s)) != PAWN) - score += ThreatByRank * (int)relative_rank(Them, s); - } + score += ThreatByRook[type_of(pos.piece_on(pop_lsb(&b)))]; if (weak & attackedBy[Us][KING]) score += ThreatByKing; diff --git a/src/search.cpp b/src/search.cpp index 2ad535cc..d34e1823 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1008,7 +1008,7 @@ moves_loop: // When in check, search starts from here && pos.advanced_pawn_push(move) && pos.pawn_passed(us, to_sq(move))) extension = ONE_PLY; - + // Castling extension if (type_of(move) == CASTLING) extension = ONE_PLY; From 005ad170c1dccdcd4ce73074302828ea22628b70 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 25 Sep 2019 21:24:05 +0200 Subject: [PATCH 58/72] Adjust reductions based on the number of threads In lazySMP it makes sense to prune a little more, as multiple threads search wider. We thus increase the prefactor of the reductions slowly as a function of the threads. The prefactor of the log(threads) term is a parameter, this pull request uses 1/2 after testing. passed STC @ 8threads: LLR: 2.96 (-2.94,2.94) [0.50,4.50] Total: 118125 W: 23151 L: 22462 D: 72512 http://tests.stockfishchess.org/tests/view/5d8bbf4d0ebc59509180f217 passed LTC @ 8threads: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 67546 W: 10630 L: 10279 D: 46637 http://tests.stockfishchess.org/tests/view/5d8c463b0ebc5950918167e8 passed ~LTC @ 14threads: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 74271 W: 12421 L: 12040 D: 49810 http://tests.stockfishchess.org/tests/view/5d8db1f50ebc590f3beb24ef Note: A larger prefactor (1) passed similar tests at STC and LTC (8 threads), while a very large one (2) passed STC quickly but failed LTC (8 threads). For the single-threaded case there is no functional change. Closes https://github.com/official-stockfish/Stockfish/pull/2337 Bench: 4088701 Fixup: remove redundant code. --- src/main.cpp | 1 - src/search.cpp | 2 +- src/thread.cpp | 3 +++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index f94a322c..40081e8d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,7 +43,6 @@ int main(int argc, char* argv[]) { Position::init(); Bitbases::init(); Endgames::init(); - Search::init(); Threads.set(Options["Threads"]); Search::clear(); // After threads are up diff --git a/src/search.cpp b/src/search.cpp index d34e1823..55df6172 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -191,7 +191,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int(23.4 * std::log(i)); + Reductions[i] = int((23.4 + std::log(Threads.size()) / 2) * std::log(i)); } diff --git a/src/thread.cpp b/src/thread.cpp index f7445f98..90ec274d 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -148,6 +148,9 @@ void ThreadPool::set(size_t requested) { // Reallocate the hash with the new threadpool size TT.resize(Options["Hash"]); + + // Init thread number dependent search params. + Search::init(); } } From e6f4b5f46344d638c5e3b27cdbc966899e80e8d6 Mon Sep 17 00:00:00 2001 From: Moez Jellouli <37274752+MJZ1977@users.noreply.github.com> Date: Fri, 27 Sep 2019 10:18:22 +0200 Subject: [PATCH 59/72] More accurate pawn attack span definition Tweak the pawn attack span for backward pawns and the zone behind opponent opposing pawns. This is important in positional play and one of weaknesses of the engine in recent high level games. STC LLR: -2.95 (-2.94,2.94) [0.50,4.50] Total: 66843 W: 14884 L: 14717 D: 37242 http://tests.stockfishchess.org/tests/view/5d8dcb1b0ebc590f3beb2956 LTC LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 77699 W: 12993 L: 12602 D: 52104 http://tests.stockfishchess.org/tests/view/5d8de9bc0ebc590f3beb3d00 See discussion in https://github.com/official-stockfish/Stockfish/pull/2332 Bench: 4012371 --- src/evaluate.cpp | 2 +- src/pawns.cpp | 25 ++++++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index fadc0fea..ee98da90 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -135,7 +135,7 @@ namespace { constexpr Score KnightOnQueen = S( 16, 12); constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score MinorBehindPawn = S( 18, 3); - constexpr Score Outpost = S( 18, 6); + constexpr Score Outpost = S( 16, 5); constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); constexpr Score RestrictedPiece = S( 7, 7); diff --git a/src/pawns.cpp b/src/pawns.cpp index 50eb3aa3..8022ae51 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -71,10 +71,10 @@ namespace { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); - Bitboard neighbours, stoppers, support, phalanx; + Bitboard neighbours, stoppers, support, phalanx, opposed; Bitboard lever, leverPush; Square s; - bool opposed, backward, passed, doubled; + bool backward, passed, doubled; Score score = SCORE_ZERO; const Square* pl = pos.squares(Us); @@ -94,8 +94,6 @@ namespace { Rank r = relative_rank(Us, s); - e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s); - // Flag the pawn opposed = theirPawns & forward_file_bb(Us, s); stoppers = theirPawns & passed_pawn_span(Us, s); @@ -112,6 +110,17 @@ namespace { backward = !(neighbours & forward_ranks_bb(Them, s)) && (stoppers & (leverPush | (s + Up))); + // Span of backward pawns and span behind opposing pawns are not included + // in the pawnAttacksSpan bitboard. + if (!backward || phalanx) + { + if (opposed) + e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s) & + ~pawn_attack_span(Us, frontmost_sq(Them, opposed)); + else + e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s); + } + // A pawn is passed if one of the three following conditions is true: // (a) there is no stoppers except some levers // (b) the only stoppers are the leverPush, but we outnumber them @@ -130,17 +139,19 @@ namespace { // Score this pawn if (support | phalanx) { - int v = Connected[r] * (2 + bool(phalanx) - opposed) + int v = Connected[r] * (2 + bool(phalanx) - bool(opposed)) + 21 * popcount(support); score += make_score(v, v * (r - 2) / 4); } else if (!neighbours) - score -= Isolated + WeakUnopposed * !opposed; + score -= Isolated + + WeakUnopposed * !opposed; else if (backward) - score -= Backward + WeakUnopposed * !opposed; + score -= Backward + + WeakUnopposed * !opposed; if (!support) score -= Doubled * doubled From 5d1568632ca42b400dff73f5e74ae613e73ed889 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sat, 24 Aug 2019 15:04:41 -0700 Subject: [PATCH 60/72] Remove temporary shelter array Remove temporary array of shelters and avoid iterating over it each time to find if the shelter values after castling are better than the current value. Work done on top of https://github.com/official-stockfish/Stockfish/pull/2277 Speed benchmark did not measure any difference. No functional change --- src/pawns.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 8022ae51..bc1cf38a 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -231,21 +231,17 @@ Score Entry::do_king_safety(const Position& pos) { Square ksq = pos.square(Us); kingSquares[Us] = ksq; castlingRights[Us] = pos.castling_rights(Us); + auto compare = [](Score a, Score b) { return mg_value(a) <= mg_value(b); }; - Score shelters[3] = { evaluate_shelter(pos, ksq), - make_score(-VALUE_INFINITE, 0), - make_score(-VALUE_INFINITE, 0) }; + Score shelter = evaluate_shelter(pos, ksq); // If we can castle use the bonus after castling if it is bigger + if (pos.can_castle(Us & KING_SIDE)) - shelters[1] = evaluate_shelter(pos, relative_square(Us, SQ_G1)); + shelter = std::max(shelter, evaluate_shelter(pos, relative_square(Us, SQ_G1)), compare); if (pos.can_castle(Us & QUEEN_SIDE)) - shelters[2] = evaluate_shelter(pos, relative_square(Us, SQ_C1)); - - for (int i : {1, 2}) - if (mg_value(shelters[i]) > mg_value(shelters[0])) - shelters[0] = shelters[i]; + shelter = std::max(shelter, evaluate_shelter(pos, relative_square(Us, SQ_C1)), compare); // In endgame we like to bring our king near our closest pawn Bitboard pawns = pos.pieces(Us, PAWN); @@ -256,7 +252,7 @@ Score Entry::do_king_safety(const Position& pos) { else while (pawns) minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns))); - return shelters[0] - make_score(0, 16 * minPawnDist); + return shelter - make_score(0, 16 * minPawnDist); } // Explicit template instantiation From 328bdd0947ea7903ee1061c8eb60bc3121a4eb19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sat, 5 Oct 2019 11:15:24 +0200 Subject: [PATCH 61/72] Fix compare function in previous patch Bench: 4012371 --- src/pawns.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index bc1cf38a..1e5b4f43 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -231,7 +231,7 @@ Score Entry::do_king_safety(const Position& pos) { Square ksq = pos.square(Us); kingSquares[Us] = ksq; castlingRights[Us] = pos.castling_rights(Us); - auto compare = [](Score a, Score b) { return mg_value(a) <= mg_value(b); }; + auto compare = [](Score a, Score b) { return mg_value(a) < mg_value(b); }; Score shelter = evaluate_shelter(pos, ksq); From ca7d4e9ac7b5ca74be8aa807fbdf139b2a0860ab Mon Sep 17 00:00:00 2001 From: Brian Sheppard Date: Sat, 28 Sep 2019 16:27:23 -0400 Subject: [PATCH 62/72] Eliminate ONE_PLY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplification that eliminates ONE_PLY, based on a suggestion in the forum that support for fractional plies has never been used, and @mcostalba's openness to the idea of eliminating it. We lose a little bit of type safety by making Depth an integer, but in return we simplify the code in search.cpp quite significantly. No functional change ------------------------------------------ The argument favoring eliminating ONE_PLY: * The term “ONE_PLY” comes up in a lot of forum posts (474 to date) https://groups.google.com/forum/?fromgroups=#!searchin/fishcooking/ONE_PLY%7Csort:relevance * There is occasionally a commit that breaks invariance of the code with respect to ONE_PLY https://groups.google.com/forum/?fromgroups=#!searchin/fishcooking/ONE_PLY%7Csort:date/fishcooking/ZIPdYj6k0fk/KdNGcPWeBgAJ * To prevent such commits, there is a Travis CI hack that doubles ONE_PLY and rechecks bench * Sustaining ONE_PLY has, alas, not resulted in any improvements to the engine, despite many individuals testing many experiments over 5 years. The strongest argument in favor of preserving ONE_PLY comes from @locutus: “If we use par example ONE_PLY=256 the parameter space is increases by the factor 256. So it seems very unlikely that the optimal setting is in the subspace of ONE_PLY=1.” There is a strong theoretical impediment to fractional depth systems: the transposition table uses depth to determine when a stored result is good enough to supply an answer for a current search. If you have fractional depths, then different pathways to the position can be at fractionally different depths. In the end, there are three separate times when a proposal to remove ONE_PLY was defeated by the suggestion to “give it a few more months.” So… it seems like time to remove this distraction from the community. See the pull request here: https://github.com/official-stockfish/Stockfish/pull/2289 --- .travis.yml | 3 - src/movepick.cpp | 6 +- src/search.cpp | 148 +++++++++++++++++++++++------------------------ src/thread.cpp | 2 +- src/tt.cpp | 8 +-- src/tt.h | 2 +- src/types.h | 17 ++---- 7 files changed, 86 insertions(+), 100 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1d56a23e..a59ea425 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,9 +56,6 @@ script: - make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref - make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref - # Verify bench number is ONE_PLY independent by doubling its value - - sed -i'.bak' 's/.*\(ONE_PLY = [0-9]*\),.*/\1 * 2,/g' types.h - - make clean && make -j2 ARCH=x86-64 build && ../tests/signature.sh $benchref # # Check perft and reproducible search - ../tests/perft.sh diff --git a/src/movepick.cpp b/src/movepick.cpp index 64380da9..fab8cea8 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -61,7 +61,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) { - assert(d > DEPTH_ZERO); + assert(d > 0); stage = pos.checkers() ? EVASION_TT : MAIN_TT; ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; @@ -73,7 +73,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs) : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), recaptureSquare(rs), depth(d) { - assert(d <= DEPTH_ZERO); + assert(d <= 0); stage = pos.checkers() ? EVASION_TT : QSEARCH_TT; ttMove = ttm @@ -206,7 +206,7 @@ top: endMoves = generate(pos, cur); score(); - partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY); + partial_insertion_sort(cur, endMoves, -4000 * depth); } ++stage; diff --git a/src/search.cpp b/src/search.cpp index 55df6172..c05e72e0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -64,15 +64,15 @@ namespace { // Razor and futility margins constexpr int RazorMargin = 661; Value futility_margin(Depth d, bool improving) { - return Value(198 * (d / ONE_PLY - improving)); + return Value(198 * (d - improving)); } // Reductions lookup table, initialized at startup int Reductions[MAX_MOVES]; // [depth or moveNumber] Depth reduction(bool i, Depth d, int mn) { - int r = Reductions[d / ONE_PLY] * Reductions[mn]; - return ((r + 520) / 1024 + (!i && r > 999)) * ONE_PLY; + int r = Reductions[d] * Reductions[mn]; + return (r + 520) / 1024 + (!i && r > 999); } constexpr int futility_move_count(bool improving, int depth) { @@ -80,8 +80,7 @@ namespace { } // History and stats update bonus, based on depth - int stat_bonus(Depth depth) { - int d = depth / ONE_PLY; + int stat_bonus(Depth d) { return d > 17 ? -8 : 22 * d * d + 151 * d - 140; } @@ -94,7 +93,7 @@ namespace { struct Skill { explicit Skill(int l) : level(l) {} bool enabled() const { return level < 20; } - bool time_to_pick(Depth depth) const { return depth / ONE_PLY == 1 + level; } + bool time_to_pick(Depth depth) const { return depth == 1 + level; } Move pick_best(size_t multiPV); int level; @@ -148,7 +147,7 @@ namespace { Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); template - Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = DEPTH_ZERO); + Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = 0); Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply); @@ -164,16 +163,16 @@ namespace { StateInfo st; uint64_t cnt, nodes = 0; - const bool leaf = (depth == 2 * ONE_PLY); + const bool leaf = (depth == 2); for (const auto& m : MoveList(pos)) { - if (Root && depth <= ONE_PLY) + if (Root && depth <= 1) cnt = 1, nodes++; else { pos.do_move(m, st); - cnt = leaf ? MoveList(pos).size() : perft(pos, depth - ONE_PLY); + cnt = leaf ? MoveList(pos).size() : perft(pos, depth - 1); nodes += cnt; pos.undo_move(m); } @@ -215,7 +214,7 @@ void MainThread::search() { if (Limits.perft) { - nodes = perft(rootPos, Limits.perft * ONE_PLY); + nodes = perft(rootPos, Limits.perft); sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl; return; } @@ -328,7 +327,7 @@ void Thread::search() { Move pv[MAX_PLY+1]; Value bestValue, alpha, beta, delta; Move lastBestMove = MOVE_NONE; - Depth lastBestMoveDepth = DEPTH_ZERO; + Depth lastBestMoveDepth = 0; MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); double timeReduction = 1, totBestMoveChanges = 0; Color us = rootPos.side_to_move(); @@ -378,9 +377,9 @@ void Thread::search() { : -make_score(ct, ct / 2)); // Iterative deepening loop until requested to stop or the target depth is reached - while ( (rootDepth += ONE_PLY) < DEPTH_MAX + while ( ++rootDepth < MAX_PLY && !Threads.stop - && !(Limits.depth && mainThread && rootDepth / ONE_PLY > Limits.depth)) + && !(Limits.depth && mainThread && rootDepth > Limits.depth)) { // Age out PV variability metric if (mainThread) @@ -409,7 +408,7 @@ void Thread::search() { selDepth = 0; // Reset aspiration window starting size - if (rootDepth >= 4 * ONE_PLY) + if (rootDepth >= 4) { Value previousScore = rootMoves[pvIdx].previousScore; delta = Value(23); @@ -429,7 +428,7 @@ void Thread::search() { int failedHighCnt = 0; while (true) { - Depth adjustedDepth = std::max(ONE_PLY, rootDepth - failedHighCnt * ONE_PLY); + Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt); bestValue = ::search(rootPos, ss, alpha, beta, adjustedDepth, false); // Bring the best move to the front. It is critical that sorting @@ -519,7 +518,7 @@ void Thread::search() { fallingEval = clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 9 * ONE_PLY < completedDepth ? 1.97 : 0.98; + timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.97 : 0.98; double reduction = (1.36 + mainThread->previousTimeReduction) / (2.29 * timeReduction); // Use part of the gained time from a previous stable move for the current move @@ -579,14 +578,13 @@ namespace { } // Dive into quiescence search when the depth reaches zero - if (depth < ONE_PLY) + if (depth <= 0) return qsearch(pos, ss, alpha, beta); assert(-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); - assert(DEPTH_ZERO < depth && depth < DEPTH_MAX); + assert(0 < depth && depth < MAX_PLY); assert(!(PvNode && cutNode)); - assert(depth / ONE_PLY * ONE_PLY == depth); Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64]; StateInfo st; @@ -683,7 +681,7 @@ namespace { // Extra penalty for early quiet moves of the previous ply if ((ss-1)->moveCount <= 2 && !pos.captured_piece()) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY)); + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1)); } // Penalty for a quiet ttMove that fails low else if (!pos.capture_or_promotion(ttMove)) @@ -730,7 +728,7 @@ namespace { || (b == BOUND_LOWER ? value >= beta : value <= alpha)) { tte->save(posKey, value_to_tt(value, ss->ply), ttPv, b, - std::min(DEPTH_MAX - ONE_PLY, depth + 6 * ONE_PLY), + std::min(MAX_PLY - 1, depth + 6), MOVE_NONE, VALUE_NONE); return value; @@ -785,7 +783,7 @@ namespace { // Step 7. Razoring (~2 Elo) if ( !rootNode // The required rootNode PV handling is not available in qsearch - && depth < 2 * ONE_PLY + && depth < 2 && eval <= alpha - RazorMargin) return qsearch(pos, ss, alpha, beta); @@ -794,7 +792,7 @@ namespace { // Step 8. Futility pruning: child node (~30 Elo) if ( !PvNode - && depth < 7 * ONE_PLY + && depth < 7 && eval - futility_margin(depth, improving) >= beta && eval < VALUE_KNOWN_WIN) // Do not return unproven wins return eval; @@ -805,7 +803,7 @@ namespace { && (ss-1)->statScore < 22661 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 33 * depth / ONE_PLY + 299 - improving * 30 + && ss->staticEval >= beta - 33 * depth + 299 - improving * 30 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -813,7 +811,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = ((835 + 70 * depth / ONE_PLY) / 256 + std::min(int(eval - beta) / 185, 3)) * ONE_PLY; + Depth R = (835 + 70 * depth) / 256 + std::min(int(eval - beta) / 185, 3); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0]; @@ -830,14 +828,14 @@ namespace { if (nullValue >= VALUE_MATE_IN_MAX_PLY) nullValue = beta; - if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 13 * ONE_PLY)) + if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 13)) return nullValue; assert(!thisThread->nmpMinPly); // Recursive verification is not allowed // Do verification search at high depths, with null move pruning disabled // for us, until ply exceeds nmpMinPly. - thisThread->nmpMinPly = ss->ply + 3 * (depth-R) / (4 * ONE_PLY); + thisThread->nmpMinPly = ss->ply + 3 * (depth-R) / 4; thisThread->nmpColor = us; Value v = search(pos, ss, beta-1, beta, depth-R, false); @@ -853,7 +851,7 @@ namespace { // If we have a good enough capture and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. if ( !PvNode - && depth >= 5 * ONE_PLY + && depth >= 5 && abs(beta) < VALUE_MATE_IN_MAX_PLY) { Value raisedBeta = std::min(beta + 191 - 46 * improving, VALUE_INFINITE); @@ -869,7 +867,7 @@ namespace { ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[pos.moved_piece(move)][to_sq(move)]; - assert(depth >= 5 * ONE_PLY); + assert(depth >= 5); pos.do_move(move, st); @@ -878,7 +876,7 @@ namespace { // If the qsearch held, perform the regular search if (value >= raisedBeta) - value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4 * ONE_PLY, !cutNode); + value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4, !cutNode); pos.undo_move(move); @@ -888,9 +886,9 @@ namespace { } // Step 11. Internal iterative deepening (~2 Elo) - if (depth >= 7 * ONE_PLY && !ttMove) + if (depth >= 7 && !ttMove) { - search(pos, ss, alpha, beta, depth - 7 * ONE_PLY, cutNode); + search(pos, ss, alpha, beta, depth - 7, cutNode); tte = TT.probe(posKey, ttHit); ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; @@ -938,13 +936,13 @@ moves_loop: // When in check, search starts from here ss->moveCount = ++moveCount; if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000) - sync_cout << "info depth " << depth / ONE_PLY + sync_cout << "info depth " << depth << " currmove " << UCI::move(move, pos.is_chess960()) << " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl; if (PvNode) (ss+1)->pv = nullptr; - extension = DEPTH_ZERO; + extension = 0; captureOrPromotion = pos.capture_or_promotion(move); movedPiece = pos.moved_piece(move); givesCheck = pos.gives_check(move); @@ -956,28 +954,28 @@ moves_loop: // When in check, search starts from here // then that move is singular and should be extended. To verify this we do // a reduced search on all the other moves but the ttMove and if the // result is lower than ttValue minus a margin then we will extend the ttMove. - if ( depth >= 6 * ONE_PLY + if ( depth >= 6 && move == ttMove && !rootNode && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ && abs(ttValue) < VALUE_KNOWN_WIN && (tte->bound() & BOUND_LOWER) - && tte->depth() >= depth - 3 * ONE_PLY + && tte->depth() >= depth - 3 && pos.legal(move)) { - Value singularBeta = ttValue - 2 * depth / ONE_PLY; - Depth halfDepth = depth / (2 * ONE_PLY) * ONE_PLY; // ONE_PLY invariant + Value singularBeta = ttValue - 2 * depth; + Depth halfDepth = depth / 2; ss->excludedMove = move; value = search(pos, ss, singularBeta - 1, singularBeta, halfDepth, cutNode); ss->excludedMove = MOVE_NONE; if (value < singularBeta) { - extension = ONE_PLY; + extension = 1; singularLMR++; - if (value < singularBeta - std::min(4 * depth / ONE_PLY, 36)) + if (value < singularBeta - std::min(4 * depth, 36)) singularLMR++; } @@ -994,27 +992,27 @@ moves_loop: // When in check, search starts from here // Check extension (~2 Elo) else if ( givesCheck && (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move))) - extension = ONE_PLY; + extension = 1; // Shuffle extension else if ( PvNode && pos.rule50_count() > 18 - && depth < 3 * ONE_PLY + && depth < 3 && ++thisThread->shuffleExts < thisThread->nodes.load(std::memory_order_relaxed) / 4) // To avoid too many extensions - extension = ONE_PLY; + extension = 1; // Passed pawn extension else if ( move == ss->killers[0] && pos.advanced_pawn_push(move) && pos.pawn_passed(us, to_sq(move))) - extension = ONE_PLY; + extension = 1; // Castling extension if (type_of(move) == CASTLING) - extension = ONE_PLY; + extension = 1; // Calculate new depth for this move - newDepth = depth - ONE_PLY + extension; + newDepth = depth - 1 + extension; // Step 14. Pruning at shallow depth (~170 Elo) if ( !rootNode @@ -1022,7 +1020,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 = moveCount >= futility_move_count(improving, depth / ONE_PLY); + moveCountPruning = moveCount >= futility_move_count(improving, depth); if ( !captureOrPromotion && !givesCheck @@ -1033,8 +1031,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); - lmrDepth /= ONE_PLY; + int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0); // Countermoves based pruning (~20 Elo) if ( lmrDepth < 4 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1) @@ -1053,7 +1050,7 @@ moves_loop: // When in check, search starts from here continue; } else if ( !(givesCheck && extension) - && !pos.see_ge(move, Value(-199) * (depth / ONE_PLY))) // (~20 Elo) + && !pos.see_ge(move, Value(-199) * depth)) // (~20 Elo) continue; } @@ -1076,7 +1073,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 + if ( depth >= 3 && moveCount > 1 + 2 * rootNode && (!rootNode || thisThread->best_move_count(move) == 0) && ( !captureOrPromotion @@ -1088,35 +1085,35 @@ moves_loop: // When in check, search starts from here // Reduction if other threads are searching this position. if (th.marked()) - r += ONE_PLY; + r++; // Decrease reduction if position is or has been on the PV if (ttPv) - r -= 2 * ONE_PLY; + r -= 2; // Decrease reduction if opponent's move count is high (~10 Elo) if ((ss-1)->moveCount > 15) - r -= ONE_PLY; + r--; // Decrease reduction if ttMove has been singularly extended - r -= singularLMR * ONE_PLY; + r -= singularLMR; if (!captureOrPromotion) { // Increase reduction if ttMove is a capture (~0 Elo) if (ttCapture) - r += ONE_PLY; + r++; // Increase reduction for cut nodes (~5 Elo) if (cutNode) - r += 2 * ONE_PLY; + r += 2; // Decrease reduction for moves that escape a capture. Filter out // castling moves, because they are coded as "king captures rook" and // hence break make_move(). (~5 Elo) else if ( type_of(move) == NORMAL && !pos.see_ge(reverse_move(move))) - r -= 2 * ONE_PLY; + r -= 2; ss->statScore = thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] @@ -1133,16 +1130,16 @@ moves_loop: // When in check, search starts from here // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) if (ss->statScore >= -99 && (ss-1)->statScore < -116) - r -= ONE_PLY; + r--; else if ((ss-1)->statScore >= -117 && ss->statScore < -144) - r += ONE_PLY; + r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 16384 * ONE_PLY; + r -= ss->statScore / 16384; } - Depth d = clamp(newDepth - r, ONE_PLY, newDepth); + Depth d = clamp(newDepth - r, 1, newDepth); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); @@ -1276,18 +1273,18 @@ moves_loop: // When in check, search starts from here // Quiet best move: update move sorting heuristics if (!pos.capture_or_promotion(bestMove)) update_quiet_stats(pos, ss, bestMove, quietsSearched, quietCount, - stat_bonus(depth + (bestValue > beta + PawnValueMg ? ONE_PLY : DEPTH_ZERO))); + stat_bonus(depth + (bestValue > beta + PawnValueMg))); - update_capture_stats(pos, bestMove, capturesSearched, captureCount, stat_bonus(depth + ONE_PLY)); + update_capture_stats(pos, bestMove, capturesSearched, captureCount, stat_bonus(depth + 1)); // Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted if ( ((ss-1)->moveCount == 1 || ((ss-1)->currentMove == (ss-1)->killers[0])) && !pos.captured_piece()) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY)); + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1)); } // Bonus for prior countermove that caused the fail low - else if ( (depth >= 3 * ONE_PLY || PvNode) + else if ( (depth >= 3 || PvNode) && !pos.captured_piece()) update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth)); @@ -1315,8 +1312,7 @@ moves_loop: // When in check, search starts from here assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); - assert(depth <= DEPTH_ZERO); - assert(depth / ONE_PLY * ONE_PLY == depth); + assert(depth <= 0); Move pv[MAX_PLY+1]; StateInfo st; @@ -1455,7 +1451,7 @@ moves_loop: // When in check, search starts from here // Detect non-capture evasions that are candidates to be pruned evasionPrunable = inCheck - && (depth != DEPTH_ZERO || moveCount > 2) + && (depth != 0 || moveCount > 2) && bestValue > VALUE_MATED_IN_MAX_PLY && !pos.capture(move); @@ -1480,7 +1476,7 @@ moves_loop: // When in check, search starts from here // Make and search the move pos.do_move(move, st, givesCheck); - value = -qsearch(pos, ss+1, -beta, -alpha, depth - ONE_PLY); + value = -qsearch(pos, ss+1, -beta, -alpha, depth - 1); pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); @@ -1707,10 +1703,10 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { { bool updated = (i <= pvIdx && rootMoves[i].score != -VALUE_INFINITE); - if (depth == ONE_PLY && !updated) + if (depth == 1 && !updated) continue; - Depth d = updated ? depth : depth - ONE_PLY; + Depth d = updated ? depth : depth - 1; Value v = updated ? rootMoves[i].score : rootMoves[i].previousScore; bool tb = TB::RootInTB && abs(v) < VALUE_MATE - MAX_PLY; @@ -1720,7 +1716,7 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { ss << "\n"; ss << "info" - << " depth " << d / ONE_PLY + << " depth " << d << " seldepth " << rootMoves[i].selDepth << " multipv " << i + 1 << " score " << UCI::value(v); @@ -1779,7 +1775,7 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { RootInTB = false; UseRule50 = bool(Options["Syzygy50MoveRule"]); - ProbeDepth = int(Options["SyzygyProbeDepth"]) * ONE_PLY; + ProbeDepth = int(Options["SyzygyProbeDepth"]); Cardinality = int(Options["SyzygyProbeLimit"]); bool dtz_available = true; @@ -1788,7 +1784,7 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { if (Cardinality > MaxCardinality) { Cardinality = MaxCardinality; - ProbeDepth = DEPTH_ZERO; + ProbeDepth = 0; } if (Cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) diff --git a/src/thread.cpp b/src/thread.cpp index 90ec274d..19687aad 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -204,7 +204,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, for (Thread* th : *this) { th->shuffleExts = th->nodes = th->tbHits = th->nmpMinPly = 0; - th->rootDepth = th->completedDepth = DEPTH_ZERO; + th->rootDepth = th->completedDepth = 0; th->rootMoves = rootMoves; th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); } diff --git a/src/tt.cpp b/src/tt.cpp index 6121b3ad..d3cd094e 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -35,24 +35,22 @@ TranspositionTable TT; // Our global transposition table void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) { - assert(d / ONE_PLY * ONE_PLY == d); - // Preserve any existing move for the same position if (m || (k >> 48) != key16) move16 = (uint16_t)m; // Overwrite less valuable entries if ( (k >> 48) != key16 - ||(d - DEPTH_OFFSET) / ONE_PLY > depth8 - 4 + || d - DEPTH_OFFSET > depth8 - 4 || b == BOUND_EXACT) { - assert((d - DEPTH_OFFSET) / ONE_PLY >= 0); + assert(d >= DEPTH_OFFSET); key16 = (uint16_t)(k >> 48); value16 = (int16_t)v; eval16 = (int16_t)ev; genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); - depth8 = (uint8_t)((d - DEPTH_OFFSET) / ONE_PLY); + depth8 = (uint8_t)(d - DEPTH_OFFSET); } } diff --git a/src/tt.h b/src/tt.h index 3a5ba5da..d087cc38 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_OFFSET; } + Depth depth() const { return (Depth)depth8 + 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 6d2c09a6..cc4008b3 100644 --- a/src/types.h +++ b/src/types.h @@ -203,22 +203,18 @@ enum Piece { extern Value PieceValue[PHASE_NB][PIECE_NB]; -enum Depth : int { +typedef int Depth; - ONE_PLY = 1, +enum : int { - DEPTH_ZERO = 0 * ONE_PLY, - DEPTH_QS_CHECKS = 0 * ONE_PLY, - DEPTH_QS_NO_CHECKS = -1 * ONE_PLY, - DEPTH_QS_RECAPTURES = -5 * ONE_PLY, + DEPTH_QS_CHECKS = 0, + DEPTH_QS_NO_CHECKS = -1, + DEPTH_QS_RECAPTURES = -5, - DEPTH_NONE = -6 * ONE_PLY, + DEPTH_NONE = -6, 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"); - enum Square : int { SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1, SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2, @@ -298,7 +294,6 @@ inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } \ inline T& operator/=(T& d, int i) { return d = T(int(d) / i); } ENABLE_FULL_OPERATORS_ON(Value) -ENABLE_FULL_OPERATORS_ON(Depth) ENABLE_FULL_OPERATORS_ON(Direction) ENABLE_INCR_OPERATORS_ON(PieceType) From 2e96c513ad6113abb6bc4fdd4962cc1f6eed3d4a Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Sat, 5 Oct 2019 10:42:36 -0400 Subject: [PATCH 63/72] Introduce separate counter-move tables for captures Enhance counter-move history table by adding a capture/no-capture dimension, depending wether the previous move was a quiet move or a capture. This doubles the size of the table but provides more accurate move ordering. STC: LLR: 2.95 (-2.94,2.94) [0.50,4.50] Total: 79702 W: 17720 L: 17164 D: 44818 http://tests.stockfishchess.org/tests/view/5d97945e0ebc590c21aa724b LTC: LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 29147 W: 4907 L: 4651 D: 19589 http://tests.stockfishchess.org/tests/view/5d97ccb90ebc590c21aa7bc0 Closes https://github.com/official-stockfish/Stockfish/pull/2344 Bench: 4131643 --- src/movepick.cpp | 12 ++++++------ src/movepick.h | 2 +- src/search.cpp | 22 +++++++++++++--------- src/thread.cpp | 10 ++++++---- src/thread.h | 2 +- 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index fab8cea8..e39f2afa 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -111,11 +111,11 @@ void MovePicker::score() { + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]; else if (Type == QUIETS) - 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[5])[pos.moved_piece(m)][to_sq(m)] / 2; + m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + + 2 * (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] + + 2 * (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)] + + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)]; else // Type == EVASIONS { @@ -206,7 +206,7 @@ top: endMoves = generate(pos, cur); score(); - partial_insertion_sort(cur, endMoves, -4000 * depth); + partial_insertion_sort(cur, endMoves, -3000 * depth); } ++stage; diff --git a/src/movepick.h b/src/movepick.h index e916514d..105c95d7 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -80,7 +80,7 @@ struct Stats : public std::array, Size> {}; /// In stats table, D=0 means that the template parameter is not used enum StatsParams { NOT_USED = 0 }; - +enum StatsType { NoCaptures, Captures }; /// ButterflyHistory records how often quiet moves have been successful or /// unsuccessful during the current search, and is used for reduction and move diff --git a/src/search.cpp b/src/search.cpp index c05e72e0..156f6d1c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -334,7 +334,8 @@ void Thread::search() { 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-i)->continuationHistory = &this->continuationHistory[0][NO_PIECE][0]; // Use as a sentinel + ss->pv = pv; bestValue = delta = alpha = -VALUE_INFINITE; @@ -595,12 +596,13 @@ namespace { Value bestValue, value, ttValue, eval, maxValue; bool ttHit, ttPv, inCheck, givesCheck, improving, doLMR; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; - Piece movedPiece; + Piece movedPiece, priorCapture; int moveCount, captureCount, quietCount, singularLMR; // Step 1. Initialize node Thread* thisThread = pos.this_thread(); inCheck = pos.checkers(); + priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); moveCount = captureCount = quietCount = singularLMR = ss->moveCount = 0; bestValue = -VALUE_INFINITE; @@ -680,7 +682,7 @@ namespace { update_quiet_stats(pos, ss, ttMove, nullptr, 0, stat_bonus(depth)); // Extra penalty for early quiet moves of the previous ply - if ((ss-1)->moveCount <= 2 && !pos.captured_piece()) + if ((ss-1)->moveCount <= 2 && !priorCapture) update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1)); } // Penalty for a quiet ttMove that fails low @@ -814,7 +816,7 @@ namespace { Depth R = (835 + 70 * depth) / 256 + std::min(int(eval - beta) / 185, 3); ss->currentMove = MOVE_NULL; - ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0]; + ss->continuationHistory = &thisThread->continuationHistory[0][NO_PIECE][0]; pos.do_null_move(st); @@ -865,7 +867,7 @@ namespace { probCutCount++; ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[pos.moved_piece(move)][to_sq(move)]; + ss->continuationHistory = &thisThread->continuationHistory[!!priorCapture][pos.moved_piece(move)][to_sq(move)]; assert(depth >= 5); @@ -1066,7 +1068,7 @@ moves_loop: // When in check, search starts from here // Update the current move (this must be done after singular extension search) ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[movedPiece][to_sq(move)]; + ss->continuationHistory = &thisThread->continuationHistory[!!priorCapture][movedPiece][to_sq(move)]; // Step 15. Make the move pos.do_move(move, st, givesCheck); @@ -1279,13 +1281,13 @@ moves_loop: // When in check, search starts from here // Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted if ( ((ss-1)->moveCount == 1 || ((ss-1)->currentMove == (ss-1)->killers[0])) - && !pos.captured_piece()) + && !priorCapture) update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1)); } // Bonus for prior countermove that caused the fail low else if ( (depth >= 3 || PvNode) - && !pos.captured_piece()) + && !priorCapture) update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth)); if (PvNode) @@ -1321,6 +1323,7 @@ moves_loop: // When in check, search starts from here Move ttMove, move, bestMove; Depth ttDepth; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; + Piece priorCapture; bool ttHit, pvHit, inCheck, givesCheck, evasionPrunable; int moveCount; @@ -1335,6 +1338,7 @@ moves_loop: // When in check, search starts from here (ss+1)->ply = ss->ply + 1; bestMove = MOVE_NONE; inCheck = pos.checkers(); + priorCapture = pos.captured_piece(); moveCount = 0; // Check for an immediate draw or maximum ply reached @@ -1472,7 +1476,7 @@ moves_loop: // When in check, search starts from here } ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[pos.moved_piece(move)][to_sq(move)]; + ss->continuationHistory = &thisThread->continuationHistory[!!priorCapture][pos.moved_piece(move)][to_sq(move)]; // Make and search the move pos.do_move(move, st, givesCheck); diff --git a/src/thread.cpp b/src/thread.cpp index 19687aad..3c9473c2 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -70,11 +70,13 @@ void Thread::clear() { mainHistory.fill(0); captureHistory.fill(0); - for (auto& to : continuationHistory) - for (auto& h : to) - h->fill(0); + for (StatsType c : { NoCaptures, Captures }) + for (auto& to : continuationHistory[c]) + for (auto& h : to) + h->fill(0); - continuationHistory[NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1); + for (StatsType c : { NoCaptures, Captures }) + continuationHistory[c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1); } /// Thread::start_searching() wakes up the thread that will start the search diff --git a/src/thread.h b/src/thread.h index 79c6e43f..0a77d5b8 100644 --- a/src/thread.h +++ b/src/thread.h @@ -71,7 +71,7 @@ public: CounterMoveHistory counterMoves; ButterflyHistory mainHistory; CapturePieceToHistory captureHistory; - ContinuationHistory continuationHistory; + ContinuationHistory continuationHistory[2]; Score contempt; }; From c78f8ddd868966b43b20ae4ef585b6cf1f7ab595 Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Sun, 6 Oct 2019 09:57:20 +0200 Subject: [PATCH 64/72] Make priorCapture a bool It is always used as a bool, so let's make it a bool straight away. We can always redefine it as a Piece in a later patch if we want to use the piece type or the piece color. No functional change. --- src/search.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 156f6d1c..6cf99938 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -594,9 +594,9 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue; - bool ttHit, ttPv, inCheck, givesCheck, improving, doLMR; + bool ttHit, ttPv, inCheck, givesCheck, improving, doLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; - Piece movedPiece, priorCapture; + Piece movedPiece; int moveCount, captureCount, quietCount, singularLMR; // Step 1. Initialize node @@ -867,7 +867,7 @@ namespace { probCutCount++; ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[!!priorCapture][pos.moved_piece(move)][to_sq(move)]; + ss->continuationHistory = &thisThread->continuationHistory[priorCapture][pos.moved_piece(move)][to_sq(move)]; assert(depth >= 5); @@ -1068,7 +1068,7 @@ moves_loop: // When in check, search starts from here // Update the current move (this must be done after singular extension search) ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[!!priorCapture][movedPiece][to_sq(move)]; + ss->continuationHistory = &thisThread->continuationHistory[priorCapture][movedPiece][to_sq(move)]; // Step 15. Make the move pos.do_move(move, st, givesCheck); @@ -1323,8 +1323,7 @@ moves_loop: // When in check, search starts from here Move ttMove, move, bestMove; Depth ttDepth; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; - Piece priorCapture; - bool ttHit, pvHit, inCheck, givesCheck, evasionPrunable; + bool ttHit, pvHit, inCheck, givesCheck, evasionPrunable, priorCapture; int moveCount; if (PvNode) @@ -1476,7 +1475,7 @@ moves_loop: // When in check, search starts from here } ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[!!priorCapture][pos.moved_piece(move)][to_sq(move)]; + ss->continuationHistory = &thisThread->continuationHistory[priorCapture][pos.moved_piece(move)][to_sq(move)]; // Make and search the move pos.do_move(move, st, givesCheck); From 7264540107b7f20d16628ac8615070fe3334f5f5 Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Mon, 7 Oct 2019 00:48:19 +0200 Subject: [PATCH 65/72] Adjust pawn span Run as a simplification a) insures that pawn attacks are always included in the pawn span (this "fixes" the case where some outpost or reachable outpost bonus were awarded on squares controlled by enemy pawns). b) compute the full span only if not "backward" or not "blocked". By looking at "blocked" instead of "opposed", we get a nice simpli- fication and the "new" outpost detection is almost identical, except a few borderline cases on rank 4. passed STC http://tests.stockfishchess.org/tests/view/5d9950730ebc5902b6cefb90 LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 79113 W: 17168 L: 17159 D: 44786 passed LTC http://tests.stockfishchess.org/tests/view/5d99d14e0ebc5902b6cf0692 LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 41286 W: 6819 L: 6731 D: 27736 See https://github.com/official-stockfish/Stockfish/pull/2348 bench: 3812891 --- src/pawns.cpp | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 1e5b4f43..1825b6e2 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -72,7 +72,7 @@ namespace { constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); Bitboard neighbours, stoppers, support, phalanx, opposed; - Bitboard lever, leverPush; + Bitboard lever, leverPush, blocked; Square s; bool backward, passed, doubled; Score score = SCORE_ZERO; @@ -83,9 +83,9 @@ namespace { Bitboard doubleAttackThem = pawn_double_attacks_bb(theirPawns); - e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0; + e->passedPawns[Us] = 0; e->kingSquares[Us] = SQ_NONE; - e->pawnAttacks[Us] = pawn_attacks_bb(ourPawns); + e->pawnAttacks[Us] = e->pawnAttacksSpan[Us] = pawn_attacks_bb(ourPawns); // Loop through all pawns of the current color and score each pawn while ((s = *pl++) != SQ_NONE) @@ -96,6 +96,7 @@ namespace { // Flag the pawn opposed = theirPawns & forward_file_bb(Us, s); + blocked = theirPawns & (s + Up); stoppers = theirPawns & passed_pawn_span(Us, s); lever = theirPawns & PawnAttacks[Us][s]; leverPush = theirPawns & PawnAttacks[Us][s + Up]; @@ -105,21 +106,13 @@ namespace { support = neighbours & rank_bb(s - Up); // A pawn is backward when it is behind all pawns of the same color on - // the adjacent files and cannot safely advance. Phalanx and isolated - // pawns will be excluded when the pawn is scored. - backward = !(neighbours & forward_ranks_bb(Them, s)) - && (stoppers & (leverPush | (s + Up))); + // the adjacent files and cannot safely advance. + backward = !(neighbours & forward_ranks_bb(Them, s + Up)) + && (stoppers & (leverPush | blocked)); - // Span of backward pawns and span behind opposing pawns are not included - // in the pawnAttacksSpan bitboard. - if (!backward || phalanx) - { - if (opposed) - e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s) & - ~pawn_attack_span(Us, frontmost_sq(Them, opposed)); - else - e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s); - } + // Compute additional span if pawn is not backward nor blocked + if (!backward && !blocked) + e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s); // A pawn is passed if one of the three following conditions is true: // (a) there is no stoppers except some levers @@ -128,7 +121,7 @@ namespace { passed = !(stoppers ^ lever) || ( !(stoppers ^ leverPush) && popcount(phalanx) >= popcount(leverPush)) - || ( stoppers == square_bb(s + Up) && r >= RANK_5 + || ( stoppers == blocked && r >= RANK_5 && (shift(support) & ~(theirPawns | doubleAttackThem))); // Passed pawns will be properly scored later in evaluation when we have From 0b0b21c608a9c096af6155e39270a29ebfba240f Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Mon, 7 Oct 2019 12:30:57 +0800 Subject: [PATCH 66/72] Tweak kingFlankAttacks factor in kingDanger Increase kingFlankAttacks factor in kingDanger from 5/16 to 6/16. Failed STC: LLR: -2.96 (-2.94,2.94) [0.00,4.00] Total: 77947 W: 16989 L: 16848 D: 44110 http://tests.stockfishchess.org/tests/view/5d9ac0280ebc5902b6cf63cd Passed LTC 1: LLR: 2.96 (-2.94,2.94) [0.00,4.00] Total: 13443 W: 2231 L: 2037 D: 9175 http://tests.stockfishchess.org/tests/view/5d9ac88d0ebc5902b6cf6ffb Passed LTC 2: LLR: 2.96 (-2.94,2.94) [0.00,4.00] Total: 23340 W: 3842 L: 3617 D: 15881 http://tests.stockfishchess.org/tests/view/5d9acf7f0ebc5902b6cf7c27 Closes https://github.com/official-stockfish/Stockfish/pull/2349 Bench: 4042155 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ee98da90..0963ddd6 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -457,7 +457,7 @@ namespace { - 873 * !pos.count(Them) - 6 * mg_value(score) / 8 + mg_value(mobility[Them] - mobility[Us]) - + 5 * kingFlankAttacks * kingFlankAttacks / 16 + + 3 * kingFlankAttacks * kingFlankAttacks / 8 - 7; // Transform the kingDanger units into a Score, and subtract it from the evaluation From 0150da5c2bd4661996b05dec4a1eca473515e9d7 Mon Sep 17 00:00:00 2001 From: Alayan Date: Mon, 7 Oct 2019 19:02:33 +0200 Subject: [PATCH 67/72] Adjust aspiration window with eval This patch changes the base aspiration window size depending on the absolute value of the previous iteration score, increasing it away from zero. This stems from the observation that the further away from zero, the more likely the evaluation is to change significantly with more depth. Conversely, a tighter aspiration window is more efficient when close to zero. A beneficial side-effect is that analysis of won positions without a quick mate is less prone to waste nodes in repeated fail-high that change the eval by tiny steps. STC: LLR: 2.96 (-2.94,2.94) [0.50,4.50] Total: 60102 W: 13327 L: 12868 D: 33907 http://tests.stockfishchess.org/tests/view/5d9a70d40ebc5902b6cf39ba LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 155553 W: 25745 L: 25141 D: 104667 http://tests.stockfishchess.org/tests/view/5d9a7ca30ebc5902b6cf4028 Future work : the values used in this patch were only a reasonable guess. Further testing should unveil more optimal values. However, the aspiration window is rather tight with a minimum of 21 internal units, so discrete integers put a practical limitation to such tweaking. More exotic experiments around the aspiration window parameters could also be tried, but efficient conditions to adjust the base aspiration window size or allow it to not be centered on the current evaluation are not obvious. The aspiration window increases after a fail-high or a fail-low is another avenue to explore for potential enhancements. Bench: 4043748 --- AUTHORS | 2 +- src/search.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index aff6a6c9..8317f545 100644 --- a/AUTHORS +++ b/AUTHORS @@ -9,7 +9,7 @@ Aditya (absimaldata) Adrian Petrescu (apetresc) Ajith Chandy Jose (ajithcj) Alain Savard (Rocky640) -alayan-stk-2 +Alayan Feh (Alayan-stk-2) Alexander Kure Alexander Pagel (Lolligerhans) Ali AlZhrani (Cooffe) diff --git a/src/search.cpp b/src/search.cpp index 6cf99938..1742c676 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -412,7 +412,7 @@ void Thread::search() { if (rootDepth >= 4) { Value previousScore = rootMoves[pvIdx].previousScore; - delta = Value(23); + delta = Value(21 + abs(previousScore) / 128); alpha = std::max(previousScore - delta,-VALUE_INFINITE); beta = std::min(previousScore + delta, VALUE_INFINITE); From 23a022980baadd5315d59a1480d26925a427aeb9 Mon Sep 17 00:00:00 2001 From: 31m059 <37052095+31m059@users.noreply.github.com> Date: Mon, 7 Oct 2019 14:47:43 -0400 Subject: [PATCH 68/72] No reachable outpost bonus for bishops Previously, we used various control statements and ternary operators to divide Outpost into four bonuses, based on whether the outpost was for a knight or bishop, and whether it was currently an Outpost or merely a potential ("reachable") one in the future. Bishop outposts, however, have traditionally been worth far less Elo in testing. An attempt to remove them altogether passed STC, but failed LTC. Here we include a narrower simplification, removing the reachable Outpost bonus for bishops. This bonus was always suspect, given that its current implementation conflicts directly with BishopPawns. BishopPawns penalizes our bishops based on the number of friendly pawns on the same color of square, but by definition, Outposts must be pawn-protected! This PR helps to alleviate this conceptual contradiction without loss of Elo and with slightly simpler code. On a code level, this allows us to simplify a ternary operator into the previous "if" block and distribute a multiplication into an existing constant Score. On a conceptual level, we retire one of the four traditional Outpost bonuses. STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 22277 W: 4882 L: 4762 D: 12633 http://tests.stockfishchess.org/tests/view/5d9aeed60ebc5902b6cf9751 LTC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 51206 W: 8353 L: 8280 D: 34573 http://tests.stockfishchess.org/tests/view/5d9af1940ebc5902b6cf9cd5 Closes https://github.com/official-stockfish/Stockfish/pull/2352 Bench: 3941591 --- src/evaluate.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 0963ddd6..a0ad09f0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -135,7 +135,7 @@ namespace { constexpr Score KnightOnQueen = S( 16, 12); constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score MinorBehindPawn = S( 18, 3); - constexpr Score Outpost = S( 16, 5); + constexpr Score Outpost = S( 32, 10); constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); constexpr Score RestrictedPiece = S( 7, 7); @@ -298,11 +298,11 @@ namespace { // Bonus if piece is on an outpost square or can reach one bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them); if (bb & s) - score += Outpost * (Pt == KNIGHT ? 4 : 2); - - else if (bb & b & ~pos.pieces(Us)) score += Outpost * (Pt == KNIGHT ? 2 : 1); + else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) + score += Outpost; + // Knight and Bishop bonus for being right behind a pawn if (shift(pos.pieces(PAWN)) & s) score += MinorBehindPawn; From 80d59eea392fc073f6d0ba29cb9a97e3d705ee58 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Tue, 8 Oct 2019 10:44:01 -0400 Subject: [PATCH 69/72] Introduce separate counter-move tables for inCheck Enhance counter-move history table by adding a inCheck dimension. This doubles the size of the table but provides more accurate move ordering. STC: (yellow) LLR: -2.94 (-2.94,2.94) [0.50,4.50] Total: 36217 W: 7790 L: 7777 D: 20650 http://tests.stockfishchess.org/tests/view/5d9b9a290ebc5902b6d04fe0 LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 36665 W: 6063 L: 5788 D: 24814 http://tests.stockfishchess.org/tests/view/5d9b9fcc0ebc5902b6d05985 Closes https://github.com/official-stockfish/Stockfish/pull/2353 Bench: 4053577 --- src/search.cpp | 10 +++++----- src/thread.cpp | 14 ++++++++------ src/thread.h | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 1742c676..7e2df215 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -334,7 +334,7 @@ void Thread::search() { std::memset(ss-7, 0, 10 * sizeof(Stack)); for (int i = 7; i > 0; i--) - (ss-i)->continuationHistory = &this->continuationHistory[0][NO_PIECE][0]; // Use as a sentinel + (ss-i)->continuationHistory = &this->continuationHistory[0][0][NO_PIECE][0]; // Use as a sentinel ss->pv = pv; @@ -816,7 +816,7 @@ namespace { Depth R = (835 + 70 * depth) / 256 + std::min(int(eval - beta) / 185, 3); ss->currentMove = MOVE_NULL; - ss->continuationHistory = &thisThread->continuationHistory[0][NO_PIECE][0]; + ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; pos.do_null_move(st); @@ -867,7 +867,7 @@ namespace { probCutCount++; ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[priorCapture][pos.moved_piece(move)][to_sq(move)]; + ss->continuationHistory = &thisThread->continuationHistory[inCheck][priorCapture][pos.moved_piece(move)][to_sq(move)]; assert(depth >= 5); @@ -1068,7 +1068,7 @@ moves_loop: // When in check, search starts from here // Update the current move (this must be done after singular extension search) ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[priorCapture][movedPiece][to_sq(move)]; + ss->continuationHistory = &thisThread->continuationHistory[inCheck][priorCapture][movedPiece][to_sq(move)]; // Step 15. Make the move pos.do_move(move, st, givesCheck); @@ -1475,7 +1475,7 @@ moves_loop: // When in check, search starts from here } ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[priorCapture][pos.moved_piece(move)][to_sq(move)]; + ss->continuationHistory = &thisThread->continuationHistory[inCheck][priorCapture][pos.moved_piece(move)][to_sq(move)]; // Make and search the move pos.do_move(move, st, givesCheck); diff --git a/src/thread.cpp b/src/thread.cpp index 3c9473c2..476f1d64 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -70,13 +70,15 @@ void Thread::clear() { mainHistory.fill(0); captureHistory.fill(0); - for (StatsType c : { NoCaptures, Captures }) - for (auto& to : continuationHistory[c]) - for (auto& h : to) - h->fill(0); + for (bool inCheck : { false, true }) + for (StatsType c : { NoCaptures, Captures }) + for (auto& to : continuationHistory[inCheck][c]) + for (auto& h : to) + h->fill(0); - for (StatsType c : { NoCaptures, Captures }) - continuationHistory[c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1); + for (bool inCheck : { false, true }) + for (StatsType c : { NoCaptures, Captures }) + continuationHistory[inCheck][c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1); } /// Thread::start_searching() wakes up the thread that will start the search diff --git a/src/thread.h b/src/thread.h index 0a77d5b8..0517afc5 100644 --- a/src/thread.h +++ b/src/thread.h @@ -71,7 +71,7 @@ public: CounterMoveHistory counterMoves; ButterflyHistory mainHistory; CapturePieceToHistory captureHistory; - ContinuationHistory continuationHistory[2]; + ContinuationHistory continuationHistory[2][2]; Score contempt; }; From b8e5092d07aaf736894d7d80b19d0185a1789084 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 1 Oct 2019 22:11:12 +0200 Subject: [PATCH 70/72] Add four positions to bench The current bench is missing a position with high 50 moves rule counter, making most 'shuffle' tests based on 50mr > N seem non-functional. This patch adds one FEN with high 50mr counter to address this issue (taken from a recent tcec game). Four new FENs: - position with high 50mr counter - tactical position with many captures, checks, extensions, fails high/low - two losses by Stockfish in the S16 bonus games against Houdini See the pull request for nice comments by @Alayan-stk-2 about each position in bench: https://github.com/official-stockfish/Stockfish/pull/2338 Bench: 4590210 --- src/benchmark.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index b23c5d17..8f30bee4 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -61,6 +61,10 @@ const vector Defaults = { "1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1", "6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1", "8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1", + "5rk1/q6p/2p3bR/1pPp1rP1/1P1Pp3/P3B1Q1/1K3P2/R7 w - - 93 90", + "4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21", + "r3k2r/3nnpbp/q2pp1p1/p7/Pp1PPPP1/4BNN1/1P5P/R2Q1RK1 w kq - 0 16", + "3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40", // 5-man positions "8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate From 472de897cb7efb66cb3518f3f4924716bd8abaee Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Fri, 18 Oct 2019 09:23:00 -0400 Subject: [PATCH 71/72] Current capture for Counter-Move history Use current capture to index the CMH table instead of prior capture. STC: LLR: 2.96 (-2.94,2.94) [0.00,4.00] Total: 61908 W: 13626 L: 13220 D: 35062 http://tests.stockfishchess.org/tests/view/5da8aa670ebc597ba8eda558 LTC: LLR: 2.96 (-2.94,2.94) [0.00,4.00] Total: 49057 W: 8071 L: 7765 D: 33221 http://tests.stockfishchess.org/tests/view/5da8e99d0ebc597ba8eda9ca Closes https://github.com/official-stockfish/Stockfish/pull/2362 Bench: 4423737 --- src/search.cpp | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7e2df215..6e59bb54 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -864,12 +864,17 @@ namespace { && probCutCount < 2 + 2 * cutNode) if (move != excludedMove && pos.legal(move)) { + assert(pos.capture_or_promotion(move)); + assert(depth >= 5); + + captureOrPromotion = true; probCutCount++; ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[inCheck][priorCapture][pos.moved_piece(move)][to_sq(move)]; - - assert(depth >= 5); + ss->continuationHistory = &thisThread->continuationHistory[inCheck] + [captureOrPromotion] + [pos.moved_piece(move)] + [to_sq(move)]; pos.do_move(move, st); @@ -900,8 +905,8 @@ namespace { moves_loop: // When in check, search starts from here const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, - nullptr, (ss-4)->continuationHistory, - nullptr, (ss-6)->continuationHistory }; + nullptr , (ss-4)->continuationHistory, + nullptr , (ss-6)->continuationHistory }; Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq]; @@ -911,7 +916,7 @@ moves_loop: // When in check, search starts from here countermove, ss->killers); - value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc + value = bestValue; moveCountPruning = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); @@ -1068,7 +1073,10 @@ moves_loop: // When in check, search starts from here // Update the current move (this must be done after singular extension search) ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[inCheck][priorCapture][movedPiece][to_sq(move)]; + ss->continuationHistory = &thisThread->continuationHistory[inCheck] + [captureOrPromotion] + [movedPiece] + [to_sq(move)]; // Step 15. Make the move pos.do_move(move, st, givesCheck); @@ -1323,7 +1331,7 @@ moves_loop: // When in check, search starts from here Move ttMove, move, bestMove; Depth ttDepth; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; - bool ttHit, pvHit, inCheck, givesCheck, evasionPrunable, priorCapture; + bool ttHit, pvHit, inCheck, givesCheck, captureOrPromotion, evasionPrunable; int moveCount; if (PvNode) @@ -1337,7 +1345,6 @@ moves_loop: // When in check, search starts from here (ss+1)->ply = ss->ply + 1; bestMove = MOVE_NONE; inCheck = pos.checkers(); - priorCapture = pos.captured_piece(); moveCount = 0; // Check for an immediate draw or maximum ply reached @@ -1408,8 +1415,8 @@ moves_loop: // When in check, search starts from here } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, - nullptr, (ss-4)->continuationHistory, - nullptr, (ss-6)->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, @@ -1426,6 +1433,7 @@ moves_loop: // When in check, search starts from here assert(is_ok(move)); givesCheck = pos.gives_check(move); + captureOrPromotion = pos.capture_or_promotion(move); moveCount++; @@ -1475,7 +1483,10 @@ moves_loop: // When in check, search starts from here } ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[inCheck][priorCapture][pos.moved_piece(move)][to_sq(move)]; + ss->continuationHistory = &thisThread->continuationHistory[inCheck] + [captureOrPromotion] + [pos.moved_piece(move)] + [to_sq(move)]; // Make and search the move pos.do_move(move, st, givesCheck); From 12d58adc68b1aa084d383d06bc47abbb3495ce3e Mon Sep 17 00:00:00 2001 From: xoto10 Date: Thu, 19 Sep 2019 17:10:46 +0100 Subject: [PATCH 72/72] Remove uithread With the current questions and issues around threading, I had a look at https://github.com/official-stockfish/Stockfish/issues/2299. It seems there was a problem with data races when requesting eval via UCI while a search was already running. To fix this an extra thread uithread was created, presumably to avoid an overlap with Threads.main() that was causing problems. Making this eval request seems to be outside the scope of UCI, and @vondele also reports that the data race is not even fixed reliably by this change. I suggest we simplify the threading here by removing this uithread and adding a comment signaling that user should not request eval when a search is already running. Closes https://github.com/official-stockfish/Stockfish/pull/2310 No functional change. --- src/uci.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index a4235f2b..99bf1a13 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -191,9 +191,8 @@ void UCI::loop(int argc, char* argv[]) { Position pos; string token, cmd; StateListPtr states(new std::deque(1)); - auto uiThread = std::make_shared(0); - pos.set(StartFEN, false, &states->back(), uiThread.get()); + pos.set(StartFEN, false, &states->back(), Threads.main()); for (int i = 1; i < argc; ++i) cmd += std::string(argv[i]) + " "; @@ -229,7 +228,8 @@ void UCI::loop(int argc, char* argv[]) { else if (token == "ucinewgame") Search::clear(); else if (token == "isready") sync_cout << "readyok" << sync_endl; - // Additional custom non-UCI commands, mainly for debugging + // Additional custom non-UCI commands, mainly for debugging. + // Do not use these commands during a search! else if (token == "flip") pos.flip(); else if (token == "bench") bench(pos, is, states); else if (token == "d") sync_cout << pos << sync_endl;